#!/usr/bin/env python ### This program is free software; you can redistribute it and/or modify ### it under the terms of the GNU Library General Public License as published by ### the Free Software Foundation; version 2 only ### ### This program is distributed in the hope that it will be useful, ### but WITHOUT ANY WARRANTY; without even the implied warranty of ### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ### GNU Library General Public License for more details. ### ### You should have received a copy of the GNU Library General Public License ### along with this program; if not, write to the Free Software ### Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ### Copyright 2004-2007 Dag Wieers #from __future__ import generators VERSION = '0.6.8' def inspath(path): if os.path.isdir(path) and path not in sys.path: sys.path.insert(1, path) try: import sys, signal, os, re, time, string import types, signal, resource, getpass inspath('/usr/local/share/dstat/') inspath('/usr/share/dstat/') inspath(os.path.abspath(os.path.dirname(sys.argv[0])) + '/plugins/') # binary path + /plugins/ inspath(os.getcwd() + '/plugins/') # current path + /plugins/ inspath(os.getcwd()) # current path inspath(os.path.expanduser('~/.dstat/')) # home + /.dstat/ except KeyboardInterrupt, e: pass #if sys.version_info < (1, 5): # sys.exit('error: Python 1.5 or later required') ### Workaround for python <= 2.2.1 try: True, False except NameError: True = 1 False = 0 ### Workaround for python < 2.3 (FIXME: check for sys.version_info existence) if True: def enumerate(sequence): index = 0 list = [] for item in sequence: list.append((index, item)) index = index + 1 return list ### Workaround for python < 2.3 if True: def sum(sequence): ret = 0 for i in sequence: ret = ret + i return ret class Options: def __init__(self, args): self.args = args self.count = -1 self.cpulist = None self.debug = 0 self.delay = 1 self.disklist = None self.full = False self.integer = False self.intlist = None self.netlist = None self.swaplist = None self.color = True self.update = True self.header = True self.output = False ### Implicit if no terminal is used if not sys.stdout.isatty(): self.color = False self.header = False self.update = False ### Temporary hardcoded for my own project self.diskset = { 'local': ('sda', 'hd[a-d]'), 'lores': ('sd[b-k]', 'sd[v-z]', 'sda[a-e]'), 'hires': ('sd[l-u]', 'sda[f-o]'), } try: import getopt opts, args = getopt.getopt (args, 'acdfghilmno:pstTvyC:D:I:M:N:S:V', ['cpu', 'disk', 'epoch', 'int', 'ipc', 'load', 'lock', 'mem', 'net', 'page', 'proc', 'raw', 'swap', 'sys', 'tcp', 'time', 'udp', 'unix', 'all', 'debug', 'full', 'help', 'integer', 'mods', 'modules', 'nocolor', 'noheaders', 'noupdate', 'output=', 'version', 'vmstat']) except getopt.error, exc: print 'dstat: %s, try dstat -h for a list of all the options' % str(exc) sys.exit(1) self.modlist = [] for opt, arg in opts: if opt in ['-c', '--cpu']: self.modlist.append('cpu') elif opt in ['-C']: self.cpulist = string.split(arg, ',') elif opt in ['-d', '--disk']: self.modlist.append('disk') elif opt in ['-D']: self.disklist = string.split(arg, ',') elif opt in ['--debug']: self.debug = self.debug + 1 elif opt in ['-g', '--page']: self.modlist.append('page') elif opt in ['-i', '--int']: self.modlist.append('int') elif opt in ['-I']: self.intlist = string.split(arg, ',') elif opt in ['--ipc']: self.modlist.append('ipc') elif opt in ['-l', '--load']: self.modlist.append('load') elif opt in ['--lock']: self.modlist.append('lock') elif opt in ['-m', '--mem']: self.modlist.append('mem') elif opt in ['-M', '--mods', '--modules']: self.modlist = self.modlist + string.split(arg, ',') elif opt in ['-n', '--net']: self.modlist.append('net') elif opt in ['-N']: self.netlist = string.split(arg, ',') elif opt in ['-p', '--proc']: self.modlist.append('proc') elif opt in ['--raw']: self.modlist.append('raw') elif opt in ['-s', '--swap']: self.modlist.append('swap') elif opt in ['-S']: self.swaplist = string.split(arg, ',') elif opt in ['--tcp']: self.modlist.append('tcp') elif opt in ['-t', '--time']: self.modlist.append('time') elif opt in ['-T', '--epoch']: self.modlist.append('epoch') elif opt in ['--udp']: self.modlist.append('udp') elif opt in ['--unix']: self.modlist.append('unix') elif opt in ['-y', '--sys']: self.modlist.append('sys') elif opt in ['-a', '--all']: self.modlist = self.modlist + [ 'cpu', 'disk', 'net', 'page', 'sys' ] elif opt in ['-v', '--vmstat']: self.modlist = self.modlist + [ 'proc', 'mem', 'page', 'disk', 'sys', 'cpu' ] elif opt in ['-f', '--full']: self.full = True elif opt in ['--integer']: self.integer = True elif opt in ['--nocolor']: self.color = False self.update = False elif opt in ['--noheaders']: self.header = False elif opt in ['--noupdate']: self.update = False elif opt in ['-o', '--output']: self.output = arg elif opt in ['-h', '--help']: self.usage() self.help() sys.exit(0) elif opt in ['-V', '--version']: self.version() sys.exit(0) if not self.modlist: self.modlist = [ 'cpu', 'disk', 'net', 'page', 'sys' ] try: if len(args) > 0: self.delay = int(args[0]) if len(args) > 1: self.count = int(args[1]) except: print 'dstat: incorrect argument, try dstat -h for the correct syntax' sys.exit(1) if self.delay <= 0: print 'dstat: delay must be an integer, greater than zero' sys.exit(1) def version(self): print 'Dstat %s' % VERSION print 'Written by Dag Wieers ' print 'Homepage at http://dag.wieers.com/home-made/dstat/' print print 'Platform %s/%s' % (os.name, sys.platform) print 'Kernel %s' % os.uname()[2] print 'Python %s' % sys.version print color = "" if not gettermcolor(self.color): color = "no " print 'Terminal type: %s (%scolor support)' % (getenv('TERM'), color) rows, cols = gettermsize() print 'Terminal size: %d lines, %d columns' % (rows, cols) print print 'Processors: %d' % getcpunr() print 'Pagesize: %d' % resource.getpagesize() print 'Clock ticks per secs: %d' % os.sysconf('SC_CLK_TCK') print global op op = self listmodules() def usage(self): print 'Usage: dstat [-afv] [options..] [delay [count]]' def help(self): print '''Versatile tool for generating system resource statistics Dstat options: -c, --cpu enable cpu stats -C 0,3,total include cpu0, cpu3 and total -d, --disk enable disk stats -D total,hda include hda and total -g, --page enable page stats -i, --int enable interrupt stats -I 5,eth2 include int5 and interrupt used by eth2 -l, --load enable load stats -m, --mem enable memory stats -n, --net enable network stats -N eth1,total include eth1 and total -p, --proc enable process stats -s, --swap enable swap stats -S swap1,total include swap1 and total -t, --time enable time/date output -T, --epoch enable time counter (seconds since epoch) -y, --sys enable system stats --ipc enable ipc stats --lock enable lock stats --raw enable raw stats --tcp enable tcp stats --udp enable udp stats --unix enable unix stats -M stat1,stat2 enable external stats --mods stat1,stat2 -a, --all equals -cdngy (default) -f, --full expand -C, -D, -I, -N and -S discovery lists -v, --vmstat equals -pmgdsc -D total --integer show integer values --nocolor disable colors (implies --noupdate) --noheaders disable repetitive headers --noupdate disable intermediate updates --output file write CSV output to file delay is the delay in seconds between each update count is the number of updates to display before exiting The default delay is 1 and count is unspecified (unlimited) ''' ### START STATS DEFINITIONS ### class dstat: ### Initialise default variables def init(self, vars=(), len=0): if vars: self.val = {}; self.cn1 = {}; self.cn2 = {} for name in vars: if len <= 1: self.val[name] = self.cn1[name] = self.cn2[name] = 0 else: self.val[name] = self.cn1[name] = self.cn2[name] = range(len) for i in range(len): self.val[name][i] = self.cn1[name][i] = self.cn2[name][i] = 0 def open(self, *files): "Open stat file descriptor" self.file = [] self.fd = [] for file in files: fd = dopen(file) if fd: self.file.append(file) self.fd.append(fd) else: raise Exception, 'Cannot open file %s.' % file def readlines(self): ret = [] for fd in self.fd: fd.seek(0) ret = ret + fd.readlines() return ret def statwidth(self): "Return complete stat width" return len(self.vars) * self.width() + len(self.vars) - 1 def width(self): "Return column width" if isinstance(self.name, types.StringType): return self.format[1] else: return len(self.nick) * self.format[1] + len(self.nick) - 1 def title(self, nr): if nr == 1: return self.title1() else: return self.title2() def title1(self): ret = ansi['darkblue'] if isinstance(self.name, types.StringType): max = self.statwidth() return ret + string.replace(string.center(self.name[0:max], max), ' ', '-') + ansi['default'] for i, name in enumerate(self.name): max = self.width() ret = ret + string.replace(string.center(name[0:max], max), ' ', '-') if i + 1 != len(self.name): if op.color: ret = ret + ansi['blue'] + char['dash'] + ansi['darkblue'] else: ret = ret + char['space'] return ret def title2(self): if isinstance(self.name, types.StringType): ret = '' for i, nick in enumerate(self.nick): ret = ret + ansi['blue'] + ansi['underline'] + string.center(nick, self.format[1]) + ansi['default'] if i + 1 != len(self.nick): ret = ret + char['space'] return ret else: ret = '' for i, name in enumerate(self.name): for j, nick in enumerate(self.nick): ret = ret + ansi['blue'] + ansi['underline'] + string.center(nick, self.format[1]) + ansi['default'] if j + 1 != len(self.nick): ret = ret + char['space'] if i + 1 != len(self.name): ret = ret + ansi['gray'] + char['colon'] return ret def titlecsv(self, nr): if nr == 1: return self.titlecsv1() else: return self.titlecsv2() def titlecsv1(self): if isinstance(self.name, types.StringType): return '"' + self.name + '"' + ',' * (len(self.nick) - 1) else: ret = '' for i, name in enumerate(self.name): ret = ret + '"' + name + '"' + ',' * (len(self.nick) - 1) if i + 1 != len(self.name): ret = ret + ',' return ret def titlecsv2(self): if isinstance(self.name, types.StringType): ret = '' for i, nick in enumerate(self.nick): ret = ret + '"' + nick + '"' if i + 1 != len(self.nick): ret = ret + ',' return ret else: ret = '' for i, name in enumerate(self.name): for j, nick in enumerate(self.nick): ret = ret + '"' + nick + '"' if j + 1 != len(self.nick): ret = ret + ',' if i + 1 != len(self.name): ret = ret + ',' return ret def check(self): "Check if stat is applicable" # if hasattr(self, 'fd') and not self.fd: # raise Exception, 'File %s does not exist' % self.file if not self.vars: raise Exception, 'No objects found, no stats available' if not self.discover: raise Exception, 'No objects discovered, no stats available' if self.width(): return True raise Exception, 'Unknown problem, please report' def discover(self): return True def show(self): "Display stat results" line = '' for i, name in enumerate(self.vars): if isinstance(self.val[name], types.TupleType) or isinstance(self.val[name], types.ListType): line = line + cprintlist(self.val[name], self.format) sep = ansi['gray'] + char['colon'] else: line = line + cprint(self.val[name], self.format) sep = char['space'] if i + 1 != len(self.vars): line = line + sep return line def showend(self, totlist, vislist): if self is not vislist[-1]: return ansi['gray'] + char['pipe'] elif totlist != vislist: return ansi['gray'] + char['gt'] return '' def showcsv(self): def printcsv(var): if var != round(var): return '%.3f' % var return '%s' % round(var) line = '' for i, name in enumerate(self.vars): if isinstance(self.val[name], types.ListType) or isinstance(self.val[name], types.TupleType): for j, val in enumerate(self.val[name]): line = line + printcsv(val) if j + 1 != len(self.val[name]): line = line + ',' elif isinstance(self.val[name], types.StringType): line = line + self.val[name] else: line = line + printcsv(self.val[name]) if i + 1 != len(self.vars): line = line + ',' return line def showcsvend(self, totlist, vislist): if self is not vislist[-1]: return ',' elif self is not totlist[-1]: return ',' return '' class dstat_cpu(dstat): def __init__(self): self.format = ('p', 3, 34) self.open('/proc/stat') self.nick = ( 'usr', 'sys', 'idl', 'wai', 'hiq', 'siq' ) self.discover = self.discover() self.vars = self.vars() self.name = self.name() self.init(self.vars + ['total',], 6) def discover(self, *list): ret = [] for line in self.readlines(): l = string.split(line) if len(l) < 8 or l[0][0:3] != 'cpu': continue ret.append(l[0][3:]) ret.sort() for item in list: ret.append(item) return ret def name(self): ret = [] for name in self.vars: if name == 'total': ret.append('total cpu usage') else: ret.append('cpu' + name + ' usage') return ret def vars(self): ret = [] if op.cpulist: list = op.cpulist elif not op.full: list = ('total',) else: list = [] cpu = 0 while cpu < cpunr: list.append(str(cpu)) cpu = cpu + 1 # if len(list) > 2: list = list[0:2] for name in list: if name in self.discover + ['total']: ret.append(name) return ret def extract(self): for line in self.readlines(): l = string.split(line) if len(l) < 8: continue for name in self.vars: if l[0] == 'cpu' + name or ( l[0] == 'cpu' and name == 'total' ): self.cn2[name] = ( long(l[1]) + long(l[2]), long(l[3]), long(l[4]), long(l[5]), long(l[6]), long(l[7]) ) for name in self.vars: for i in range(6): self.val[name][i] = 100.0 * (self.cn2[name][i] - self.cn1[name][i]) / (sum(self.cn2[name]) - sum(self.cn1[name])) if step == op.delay: self.cn1.update(self.cn2) class dstat_cpu24(dstat): def __init__(self): self.format = ('p', 3, 34) self.open('/proc/stat') self.nick = ( 'usr', 'sys', 'idl') self.discover = self.discover() self.vars = self.vars() self.name = self.name() self.init(self.vars + ['total',], 3) def name(self): ret = [] for name in self.vars: if name: ret.append('cpu' + name) else: ret.append('cpu total') return ret def discover(self, *list): ret = [] for line in self.readlines(): l = string.split(line) if len(l) != 5 or l[0][0:3] != 'cpu': continue ret.append(l[0][3:]) ret.sort() for item in list: ret.append(item) return ret def vars(self): ret = [] if op.cpulist: list = op.cpulist elif not op.full: list = ('total',) else: list = [] cpu = 0 while cpu < cpunr: list.append(str(cpu)) cpu = cpu + 1 # if len(list) > 2: list = list[0:2] for name in list: if name in self.discover + ['total']: ret.append(name) return ret def extract(self): for line in self.readlines(): l = string.split(line) for name in self.vars: if l[0] == 'cpu' + name or ( l[0] == 'cpu' and name == 'total' ): self.cn2[name] = ( long(l[1]) + long(l[2]), long(l[3]), long(l[4]) ) for name in self.vars: for i in range(3): self.val[name][i] = 100.0 * (self.cn2[name][i] - self.cn1[name][i]) / (sum(self.cn2[name]) - sum(self.cn1[name])) if step == op.delay: self.cn1.update(self.cn2) class dstat_disk(dstat): def __init__(self): self.format = ('f', 5, 1024) self.open('/proc/diskstats') self.nick = ('read', 'writ') self.discover = self.discover() self.vars = self.vars() self.name = [] for name in self.vars: self.name.append('dsk/' + name) self.init(self.vars + ['total',], 2) def discover(self, *list): ret = [] for line in self.readlines(): l = string.split(line) if len(l) < 13: continue if l[3:] == ['0',] * 11: continue name = l[2] ret.append(name) for item in list: ret.append(item) return ret def vars(self): ret = [] if op.disklist: list = op.disklist elif not op.full: list = ('total',) else: list = [] for name in self.discover: if not re.match('(md[0-9]+|dm-[0-9]+)', name): list.append(name) # if len(list) > 2: list = list[0:2] list.sort() for name in list: if name in self.discover + ['total'] + op.diskset.keys(): ret.append(name) return ret def extract(self): for name in self.vars: self.cn2[name] = (0, 0) for line in self.readlines(): l = string.split(line) if len(l) < 13: continue if l[5] == '0' and l[9] == '0': continue name = l[2] if l[3:] == ['0',] * 11: continue if not re.match('(md[0-9]+|dm-[0-9]+)', name): self.cn2['total'] = ( self.cn2['total'][0] + long(l[5]), self.cn2['total'][1] + long(l[9]) ) if name in self.vars and name != 'total': self.cn2[name] = ( self.cn2[name][0] + long(l[5]), self.cn2[name][1] + long(l[9]) ) for set in self.vars: if set in op.diskset.keys(): for disk in op.diskset[set]: if re.match('^'+disk+'$', name): self.cn2[set] = ( self.cn2[set][0] + long(l[5]), self.cn2[set][1] + long(l[9]) ) for name in self.cn2.keys(): self.val[name] = ( (self.cn2[name][0] - self.cn1[name][0]) * 512.0 / tick, (self.cn2[name][1] - self.cn1[name][1]) * 512.0 / tick, ) if step == op.delay: self.cn1.update(self.cn2) class dstat_disk24(dstat): def __init__(self): self.format = ('f', 5, 1024) self.open('/proc/partitions') self.nick = ('read', 'writ') self.discover = self.discover() self.vars = self.vars() if self.fd and not self.discover: raise Exception, 'Kernel is not compiled with CONFIG_BLK_STATS' self.name = [] for name in self.vars: self.name.append('dsk/' + sysfs_dev(name)) self.init(self.vars + ['total',], 2) def discover(self, *list): ret = [] for line in self.readlines(): l = string.split(line) if len(l) < 15 or l[0] == 'major' or int(l[1]) % 16 != 0: continue name = l[3] ret.append(name) for item in list: ret.append(item) return ret def vars(self): ret = [] if op.disklist: list = op.disklist elif not op.full: list = ('total',) else: list = [] for name in self.discover: if not re.match('(md[0-9]+|dm-[0-9]+)', name): list.append(name) # if len(list) > 2: list = list[0:2] list.sort() for name in list: if name in self.discover + ['total'] + op.diskset.keys(): ret.append(name) return ret def extract(self): for name in self.vars: self.cn2[name] = (0, 0) for line in self.readlines(): l = string.split(line) if len(l) < 15 or l[0] == 'major' or int(l[1]) % 16 != 0: continue name = l[3] if not re.match('(md[0-9]+|dm-[0-9]+)', name): self.cn2['total'] = ( self.cn2['total'][0] + long(l[6]), self.cn2['total'][1] + long(l[10]) ) if name in self.vars: self.cn2[name] = ( self.cn2[name][0] + long(l[6]), self.cn2[name][1] + long(l[10]) ) for set in self.vars: if set in op.diskset.keys(): for disk in op.diskset[set]: if re.match('^'+disk+'$', name): self.cn2[set] = ( self.cn2[set][0] + long(l[6]), self.cn2[set][1] + long(l[10]) ) for name in self.cn2.keys(): self.val[name] = ( (self.cn2[name][0] - self.cn1[name][0]) * 512.0 / tick, (self.cn2[name][1] - self.cn1[name][1]) * 512.0 / tick, ) if step == op.delay: self.cn1.update(self.cn2) ### FIXME: Needs rework, does anyone care ? class dstat_disk24old(dstat): def __init__(self): self.format = ('f', 5, 1024) self.open('/proc/stat') self.nick = ('read', 'writ') self.regexp = re.compile('^\((\d+),(\d+)\):\(\d+,\d+,(\d+),\d+,(\d+)\)$') self.discover = self.discover() self.vars = self.vars() self.name = [] for name in self.vars: self.name.append('dsk/' + name) self.init(self.vars + ['total',], 2) def discover(self, *list): ret = [] for line in self.readlines(): l = string.split(line, ':') if len(l) < 3: continue name = l[0] if name != 'disk_io': continue for pair in string.split(line)[1:]: m = self.regexp.match(pair) if not m: continue l = m.groups() if len(l) < 4: continue name = dev(int(l[0]), int(l[1])) ret.append(name) break for item in list: ret.append(item) return ret def vars(self): ret = [] if op.disklist: list = op.disklist elif not op.full: list = ('total',) else: list = [] for name in self.discover: if not re.match('(md[0-9]+|dm-[0-9]+)', name): list.append(name) # if len(list) > 2: list = list[0:2] list.sort() for name in list: if name in self.discover + ['total'] + op.diskset.keys(): ret.append(name) return ret def extract(self): for name in self.vars: self.cn2[name] = (0, 0) for line in self.readlines(): l = string.split(line, ':') if len(l) < 3: continue name = l[0] if name != 'disk_io': continue for pair in string.split(line)[1:]: m = self.regexp.match(pair) if not m: continue l = m.groups() if len(l) < 4: continue name = dev(int(l[0]), int(l[1])) if not re.match('(md[0-9]+)', name): self.cn2['total'] = ( self.cn2['total'][0] + long(l[2]), self.cn2['total'][1] + long(l[3]) ) if name in self.vars and name != 'total': self.cn2[name] = ( self.cn2[name][0] + long(l[2]), self.cn2[name][1] + long(l[3]) ) for set in self.vars: if set in op.diskset.keys(): for disk in op.diskset[set]: if re.match('^'+disk+'$', name): self.cn2[set] = ( self.cn2[set][0] + long(l[2]), self.cn2[set][1] + long(l[3]) ) break for name in self.cn2.keys(): self.val[name] = ( (self.cn2[name][0] - self.cn1[name][0]) * 512.0 / tick, (self.cn2[name][1] - self.cn1[name][1]) * 512.0 / tick, ) if step == op.delay: self.cn1.update(self.cn2) class dstat_epoch(dstat): def __init__(self): self.name = 'epoch' self.format = ('t', 10, 0) if op.debug: self.format = ('t', 14, 0) self.nick = ('epoch',) self.vars = self.nick self.init(self.vars, 1) def extract(self): self.val['epoch'] = time.time() # def show(self): # return ansi['reset'] + ( '%10.2f' % self.val['epoch'] ) ### FIXME: Make total work as well class dstat_int(dstat): def __init__(self): self.name = 'interrupts' self.format = ('d', 5, 1000) self.open('/proc/stat') self.discover = self.discover() self.intmap = self.intmap() self.vars = self.vars() self.nick = self.vars self.init(self.vars + ['total'], 1) def intmap(self): ret = {} for line in dopen('/proc/interrupts').readlines(): l = string.split(line) if len(l) <= cpunr: continue l1 = string.split(l[0], ':')[0] l2 = string.split(string.join(l[cpunr+2:], ' '), ',') ret[l1] = l1 for name in l2: name = string.lower(string.strip(name)) ret[name] = l1 return ret def discover(self): ret = [] for line in self.readlines(): l = string.split(line) if l[0] != 'intr': continue for name, i in enumerate(l[2:]): if long(i) > 10: ret.append(str(name)) return ret # def check(self): # if self.fd[0] and self.vars: # self.fd[0].seek(0) # for line in self.fd[0].readlines(): # l = string.split(line) # if l[0] != 'intr': continue # return True # return False def vars(self): ret = [] if op.intlist: list = op.intlist else: list = self.discover + ['total'] for name in list: if name in ('0', '1', '2', '8', 'NMI', 'LOC', 'MIS', 'CPU0'): list.remove(name) if not op.full and len(list) > 3: list = list[-3:] for name in list: if name in self.discover: ret.append(name) elif string.lower(name) in self.intmap.keys(): ret.append(self.intmap[string.lower(name)]) return ret def extract(self): for line in self.readlines(): l = string.split(line) if not l or l[0] != 'intr': continue for name in self.vars: self.cn2[name] = long(l[int(name) + 2]) self.cn2['total'] = self.cn2['total'] + long(l[int(name) + 2]) for name in self.vars + ['total']: self.val[name] = (self.cn2[name] - self.cn1[name]) * 1.0 / tick if step == op.delay: self.cn1.update(self.cn2) class dstat_int24(dstat): def __init__(self): self.name = 'interrupts' self.format = ('d', 5, 1000) self.open('/proc/interrupts') self.discover = self.discover() self.vars = self.vars() self.nick = self.vars self.init(self.vars, 1) def intmap(self): ret = {} for line in self.readlines(): l = string.split(line) if len(l) <= cpunr: continue l1 = string.split(l[0], ':')[0] l2 = string.split(string.join(l[cpunr+2:], ' '), ',') ret[l1] = l1 for name in l2: name = string.lower(string.strip(name)) ret[name] = l1 return ret def discover(self): ret = [] for line in self.readlines(): l = string.split(line) if len(l) < cpunr+1: continue name = string.split(l[0], ':')[0] if long(l[1]) > 10: ret.append(name) return ret # def check(self): # if self.fd and self.discover: # self.fd[0].seek(0) # for line in self.fd[0].readlines(): # l = string.split(line) # if l[0] != 'intr' or len(l) > 2: continue # return True # return False def vars(self): ret = [] if op.intlist: list = op.intlist else: list = self.discover for name in list: if name in ('0', '1', '2', '8', 'CPU0', 'ERR', 'LOC', 'MIS', 'NMI'): list.remove(name) if not op.full and len(list) > 3: list = list[-3:] for name in list: if name in self.discover: ret.append(name) elif string.lower(name) in self.intmap.keys(): ret.append(self.intmap[string.lower(name)]) return ret def extract(self): for line in self.readlines(): l = string.split(line) if len(l) < cpunr+1: continue name = string.split(l[0], ':')[0] if name in self.vars: self.cn2[name] = 0 for i in l[1:1+cpunr]: self.cn2[name] = self.cn2[name] + long(i) # elif len(l) > 2 + cpunr: # for hw in self.vars: # for mod in l[2+cpunr:]: # self.cn2[mod] = long(l[1]) for name in self.cn2.keys(): self.val[name] = (self.cn2[name] - self.cn1[name]) * 1.0 / tick if step == op.delay: self.cn1.update(self.cn2) class dstat_ipc(dstat): def __init__(self): self.name = 'sysv ipc' self.format = ('d', 3, 10) self.vars = ('msg', 'sem', 'shm') self.nick = self.vars self.init(self.vars, 1) def extract(self): for name in self.vars: self.val[name] = len(dopen('/proc/sysvipc/'+name).readlines()) - 1 class dstat_load(dstat): def __init__(self): self.name = 'load avg' self.format = ('f', 4, 10) self.open('/proc/loadavg') self.nick = ('1m', '5m', '15m') self.vars = ('load1', 'load5', 'load15') self.init(self.vars, 1) def extract(self): for line in self.readlines(): l = string.split(line) if len(l) < 3: continue self.val['load1'] = float(l[0]) self.val['load5'] = float(l[1]) self.val['load15'] = float(l[2]) class dstat_lock(dstat): def __init__(self): self.name = 'file locks' self.format = ('f', 3, 10) self.open('/proc/locks') self.nick = ('pos', 'lck', 'rea', 'wri') self.vars = ('posix', 'flock', 'read', 'write') self.init(self.vars, 1) def extract(self): for name in self.vars: self.val[name] = 0 for line in self.readlines(): l = string.split(line) if len(l) < 4: continue if l[1] == 'POSIX': self.val['posix'] = self.val['posix'] + 1 elif l[1] == 'FLOCK': self.val['flock'] = self.val['flock'] + 1 if l[3] == 'READ': self.val['read'] = self.val['read'] + 1 elif l[3] == 'WRITE': self.val['write'] = self.val['write'] + 1 class dstat_mem(dstat): def __init__(self): self.name = 'memory usage' self.format = ('f', 5, 1024) self.open('/proc/meminfo') self.nick = ('used', 'buff', 'cach', 'free') self.vars = ('MemUsed', 'Buffers', 'Cached', 'MemFree') self.init(self.vars, 1) def extract(self): for line in self.readlines(): l = string.split(line) if len(l) < 2: continue name = string.split(l[0], ':')[0] if name in self.vars + ('MemTotal', ): self.val[name] = long(l[1]) * 1024.0 self.val['MemUsed'] = self.val['MemTotal'] - self.val['MemFree'] - self.val['Buffers'] - self.val['Cached'] class dstat_net(dstat): def __init__(self): self.format = ('f', 5, 1024) self.open('/proc/net/dev') self.nick = ('recv', 'send') self.discover = self.discover() self.vars = self.vars() self.name = [] for name in self.vars: self.name.append('net/' + name) self.init(self.vars + ['total',], 2) def discover(self, *list): ret = [] for line in self.readlines(): l = string.split(string.replace(line, ':', ' ')) if len(l) < 17: continue if l[2] == '0' and l[10] == '0': continue name = l[0] if name not in ('lo', 'face'): ret.append(name) ret.sort() for item in list: ret.append(item) return ret def vars(self): ret = [] if op.netlist: list = op.netlist elif not op.full: list = ('total',) else: list = self.discover # if len(list) > 2: list = list[0:2] list.sort() for name in list: if name in self.discover + ['total', 'lo']: ret.append(name) return ret def extract(self): self.cn2['total'] = [0, 0] for line in self.readlines(): l = string.split(string.replace(line, ':', ' ')) if len(l) < 17: continue if l[2] == '0' and l[10] == '0': continue name = l[0] if name in self.vars : self.cn2[name] = ( long(l[1]), long(l[9]) ) if name not in ('lo','face'): self.cn2['total'] = ( self.cn2['total'][0] + long(l[1]), self.cn2['total'][1] + long(l[9])) if update: for name in self.cn2.keys(): self.val[name] = ( (self.cn2[name][0] - self.cn1[name][0]) * 1.0 / tick, (self.cn2[name][1] - self.cn1[name][1]) * 1.0 / tick, ) if step == op.delay: self.cn1.update(self.cn2) class dstat_page(dstat): def __init__(self): self.name = 'paging' self.format = ('f', 5, 1024) self.open('/proc/vmstat') self.nick = ('in', 'out') self.vars = ('pswpin', 'pswpout') self.init(self.vars, 1) def extract(self): for line in self.readlines(): l = string.split(line) if len(l) < 2: continue name = l[0] if name in self.vars: self.cn2[name] = long(l[1]) for name in self.vars: self.val[name] = (self.cn2[name] - self.cn1[name]) * pagesize * 1.0 / tick if step == op.delay: self.cn1.update(self.cn2) class dstat_page24(dstat): def __init__(self): self.name = 'paging' self.format = ('f', 5, 1024) self.open('/proc/stat') self.nick = ('in', 'out') self.vars = ('pswpin', 'pswpout') self.init(self.vars, 1) def extract(self): for line in self.readlines(): l = string.split(line) if len(l) < 3: continue name = l[0] if name != 'swap': continue self.cn2['pswpin'] = long(l[1]) self.cn2['pswpout'] = long(l[2]) break for name in self.vars: self.val[name] = (self.cn2[name] - self.cn1[name]) * pagesize * 1.0 / tick if step == op.delay: self.cn1.update(self.cn2) class dstat_proc(dstat): def __init__(self): self.name = 'procs' self.format = ('f', 3, 10) self.open('/proc/stat') self.nick = ('run', 'blk', 'new') self.vars = ('procs_running', 'procs_blocked', 'processes') self.init(self.vars, 1) def extract(self): for line in self.readlines(): l = string.split(line) if len(l) < 2: continue name = l[0] if name == 'processes': self.val['processes'] = 0 self.cn2[name] = long(l[1]) elif name == 'procs_running': self.cn2[name] = self.cn2[name] + long(l[1]) - 1 elif name == 'procs_blocked': self.cn2[name] = self.cn2[name] + long(l[1]) self.val['processes'] = (self.cn2['processes'] - self.cn1['processes']) * 1.0 / tick for name in ('procs_running', 'procs_blocked'): self.val[name] = self.cn2[name] * 1.0 / tick if step == op.delay: self.cn1.update(self.cn2) for name in ('procs_running', 'procs_blocked'): self.cn2[name] = 0 class dstat_raw(dstat): def __init__(self): self.name = 'raw' self.format = ('f', 3, 10) self.open('/proc/net/raw') self.nick = ('soc',) self.vars = ('sockets',) self.init(self.vars, 1) def extract(self): self.val['sockets'] = len(self.readlines()) - 1 class dstat_swap(dstat): def __init__(self): self.name = 'swap' self.format = ('f', 5, 1024) self.open('/proc/swaps') self.nick = ('used', 'free') self.discover = self.discover() self.vars = self.vars() self.name = [] for name in self.vars: self.name.append('swp/'+improve(name)) self.init(self.vars + ['total',], 2) def discover(self, *list): ret = [] for line in self.readlines(): l = string.split(line) if len(l) < 5: continue if l[0] == 'Filename': continue # ret.append(improve(l[0])) ret.append(l[0]) ret.sort() for item in list: ret.append(item) return ret def vars(self): ret = [] if op.swaplist: list = op.swaplist elif not op.full: list = ('total',) else: list = self.discover # if len(list) > 2: list = list[0:2] list.sort() for name in list: if name in self.discover + ['total']: ret.append(name) return ret def extract(self): self.val['total'] = [0, 0] for line in self.readlines(): l = string.split(line) if len(l) < 5 or l[0] == 'Filename': continue name = l[0] self.val[name] = ( long(l[3]) * 1024.0, (long(l[2]) - long(l[3])) * 1024.0 ) self.val['total'] = ( self.val['total'][0] + self.val[name][0], self.val['total'][1] + self.val[name][1]) class dstat_swapold(dstat): def __init__(self): self.name = 'swap' self.format = ('f', 5, 1024) self.open('/proc/meminfo') self.nick = ('used', 'free') self.vars = ('SwapUsed', 'SwapFree') self.init(self.vars, 1) def extract(self): for line in self.readlines(): l = string.split(line) if len(l) < 2: continue name = string.split(l[0], ':')[0] if name in self.vars + ('SwapTotal',): self.val[name] = long(l[1]) * 1024.0 self.val['SwapUsed'] = self.val['SwapTotal'] - self.val['SwapFree'] class dstat_sys(dstat): def __init__(self): self.name = 'system' self.format = ('d', 5, 1000) self.open('/proc/stat') self.nick = ('int', 'csw') self.vars = ('intr', 'ctxt') self.init(self.vars, 1) def extract(self): for line in self.readlines(): l = string.split(line) if len(l) < 2: continue name = l[0] if name in self.vars: self.cn2[name] = long(l[1]) for name in self.vars: self.val[name] = (self.cn2[name] - self.cn1[name]) * 1.0 / tick if step == op.delay: self.cn1.update(self.cn2) class dstat_tcp(dstat): def __init__(self): self.name = 'tcp sockets' self.format = ('f', 3, 100) self.open('/proc/net/tcp', '/proc/net/tcp6') self.nick = ('lis', 'act', 'syn', 'tim', 'clo') self.vars = ('listen', 'established', 'syn', 'wait', 'close') self.init(self.vars, 1) def extract(self): for name in self.vars: self.val[name] = 0 for line in self.readlines(): l = string.split(line) if len(l) < 12: continue ### 01: established, 02: syn_sent, 03: syn_recv, 04: fin_wait1, ### 05: fin_wait2, 06: time_wait, 07: close, 08: close_wait, ### 09: last_ack, 0A: listen, 0B: closing if l[3] in ('0A',): self.val['listen'] = self.val['listen'] + 1 elif l[3] in ('01',): self.val['established'] = self.val['established'] + 1 elif l[3] in ('02', '03', '09',): self.val['syn'] = self.val['syn'] + 1 elif l[3] in ('06',): self.val['wait'] = self.val['wait'] + 1 elif l[3] in ('04', '05', '07', '08', '0B',): self.val['close'] = self.val['close'] + 1 class dstat_time(dstat): def __init__(self): self.name = 'time' self.format = ('s', 14, 0) self.nick = ('date/time',) self.vars = ('time',) self.init(self.vars, 1) def extract(self): ### FIXME: Add milliseconds when using --debug (see dstat_epoch) self.val['time'] = time.strftime('%d-%m %H:%M:%S', time.localtime()) def show(self): if step == op.delay: color = 'silver' else: color = 'gray' return ansi[color] + self.val['time'] class dstat_udp(dstat): def __init__(self): self.name = 'udp' self.format = ('f', 3, 100) self.open('/proc/net/udp', '/proc/net/udp6') self.nick = ('lis', 'act') self.vars = ('listen', 'established') self.init(self.vars, 1) def extract(self): for name in self.vars: self.val[name] = 0 for line in self.readlines(): l = string.split(line) if l[3] == '07': self.val['listen'] = self.val['listen'] + 1 elif l[3] == '01': self.val['established'] = self.val['established'] + 1 class dstat_unix(dstat): def __init__(self): self.name = 'unix sockets' self.format = ('d', 4, 100) self.open('/proc/net/unix') self.nick = ('dgrm', 'strm', 'lis', 'act') self.vars = ('datagram', 'stream', 'listen', 'established') self.init(self.vars, 1) def extract(self): for name in self.vars: self.val[name] = 0 for line in self.readlines(): l = string.split(line) if l[4] == '0002': self.val['datagram'] = self.val['datagram'] + 1 elif l[4] == '0001': self.val['stream'] = self.val['stream'] + 1 if l[5] == '01': self.val['listen'] = self.val['listen'] + 1 elif l[5] == '03': self.val['established'] = self.val['established'] + 1 ### END STATS DEFINITIONS ### ansi = { 'black': '\033[0;30m', 'darkred': '\033[0;31m', 'darkgreen': '\033[0;32m', 'darkyellow': '\033[0;33m', 'darkblue': '\033[0;34m', 'darkmagenta': '\033[0;35m', 'darkcyan': '\033[0;36m', 'silver': '\033[0;37m', 'gray': '\033[1;30m', 'red': '\033[1;31m', 'green': '\033[1;32m', 'yellow': '\033[1;33m', 'blue': '\033[1;34m', 'magenta': '\033[1;35m', 'cyan': '\033[1;36m', 'white': '\033[1;37m', 'blackbg': '\033[40m', 'redbg': '\033[41m', 'greenbg': '\033[42m', 'yellowbg': '\033[43m', 'bluebg': '\033[44m', 'magentabg': '\033[45m', 'cyanbg': '\033[46m', 'whitebg': '\033[47m', 'reset': '\033[0;0m', 'bold': '\033[1m', 'reverse': '\033[2m', 'underline': '\033[4m', 'clear': '\033[2J', # 'clearline': '\033[K', 'clearline': '\033[2K', # 'save': '\033[s', # 'restore': '\033[u', 'save': '\0337', 'restore': '\0338', 'up': '\033[1A', 'down': '\033[1B', 'right': '\033[1C', 'left': '\033[1D', 'default': '\033[0;0m', } char = { 'pipe': '|', 'colon': ':', 'gt': '>', 'space': ' ', 'dash': '-', } def ticks(): "Return the number of 'ticks' since bootup" try: for line in open('/proc/uptime', 'r', 0).readlines(): l = string.split(line) if len(l) < 2: continue return float(l[0]) except: for line in dopen('/proc/stat').readlines(): l = string.split(line) if len(l) < 2: continue if l[0] == 'btime': return time.time() - long(l[1]) def improve(str): "Improve a device name" if str.startswith('/dev/mapper/'): str = string.split(str, '/')[3] elif str.startswith('/dev/'): str = string.split(str, '/')[2] return str def dopen(file): "Open a file for reuse, if already opened, return file descriptor" global fds if not os.path.exists(file): return None if 'fds' not in globals().keys(): fds = {} if file not in fds.keys(): fds[file] = open(file, 'r', 0) else: fds[file].seek(0) return fds[file] def dclose(file): "Close an open file and remove file descriptor from list" global fds if not 'fds' in globals(): fds = {} if file in fds: fds[file].close() del(fds[file]) def dpopen(cmd): "Open a pipe for reuse, if already opened, return pipes" global pipes if 'pipes' not in globals().keys(): pipes = {} if cmd not in pipes.keys(): pipes[cmd] = os.popen3(cmd, 't', 0) return pipes[cmd] def readpipe(file, tmout = 0.001): "Read available data from pipe in a non-blocking fashion" ret = '' while not select.select([file.fileno()], [], [], tmout)[0]: pass while select.select([file.fileno()], [], [], tmout)[0]: ret = ret + file.read(1) return string.split(ret, '\n') def greppipe(file, str, tmout = 0.001): "Grep available data from pipe in a non-blocking fashion" ret = '' while not select.select([file.fileno()], [], [], tmout)[0]: pass while select.select([file.fileno()], [], [], tmout)[0]: char = file.read(1) if char != '\n': ret = ret + char elif ret.startswith(str): return ret else: ret = '' return None def matchpipe(file, string, tmout = 0.001): "Match available data from pipe in a non-blocking fashion" ret = '' regexp = re.compile(string) while not select.select([file.fileno()], [], [], tmout)[0]: pass while select.select([file.fileno()], [], [], tmout)[0]: char = file.read(1) if char != '\n': ret = ret + char elif regexp.match(ret): return ret else: ret = '' return None def dchg(var, max, base): "Convert decimal to string given base and length" c = 0 while True: ret = str(int(round(var))) if len(ret) <= max: break var = var / base c = c + 1 else: c = -1 return ret, c def fchg(var, max, base): "Convert float to string given base and length" c = 0 while True: if var == 0: ret = str('0') break # ret = repr(round(var)) # ret = repr(long(round(var,max))) ret = str(long(round(var,max))) if len(ret) <= max: i = max - len(ret) while i > 0: ret = ('%.'+str(i)+'f') % var if len(ret) < max and ret != repr(round(var)): break i = i - 1 else: ret = str(int(round(var))) break var = var / base c = c + 1 else: c = -1 return ret, c def cprintlist(list, format): ret = sep = '' for var in list: ret = ret + sep + cprint(var, format) sep = ' ' return ret def cprint(var, format = ('f', 4, 1000)): c = -1 type = format[0] max = format[1] mp = format[2] base = 1000 if mp == 1024: base = 1024 unit = False if mp in (1000, 1024) and max >= len(str(base)): unit = True max = max - 1 if var < 0: if unit: return ansi['default'] + string.rjust('-', max) + ' ' else: return ansi['default'] + string.rjust('-', max) if base == 1024: units = ('B', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y') else: units = (' ', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y') if step == op.delay: colors = ('red', 'yellow', 'green', 'blue', 'magenta', 'cyan', 'white', 'darkred', 'darkgreen') else: colors = ('darkred', 'darkyellow', 'darkgreen', 'darkblue', 'darkmagenta', 'darkcyan', 'silver', 'red', 'green') if op.integer and type in ('d', 'p', 'f'): ret, c = dchg(var, max, base) elif type in ('d', 'p'): ret, c = dchg(var, max, base) elif type in ('f'): ret, c = fchg(var, max, base) elif type in ('t'): ret, c = fchg(var, max+1, base) else: ret = str(var) if ret == '0': color = 'default' elif type in ('d', 'p'): color = colors[int(var/mp)%len(colors)] elif type in ('f'): color = colors[c%len(colors)] else: color = 'default' if type in ('s',): ret = ansi['default'] + string.ljust(ret, max) else: ret = ansi[color] + string.rjust(ret, max) if unit: if c != -1 and round(var) != 0: ret = ret + units[c] else: ret = ret + ' ' return ret def showtitle(nr, totlist, vislist, midchar, endchar): line = '' for o in vislist: line = line + o.title(nr) if o is not vislist[-1]: line = line + midchar elif totlist != vislist: line = line + endchar sys.stdout.write(line + '\n') def showcsvtitle(nr, totlist): line = '' for o in totlist: line = line + o.titlecsv(nr) if o is not totlist[-1]: line = line + ',' outputfile.write(line + '\n') def info(level, str): "Output info message" # if level <= op.verbose: print str def die(ret, str): "Print error and exit with errorcode" info(0, str) exit(ret) def getenv(key): if os.environ.has_key(key): return os.environ[key] return None def initterm(): "Initialise terminal" global termsize termsize = None, None ### Unbuffered sys.stdout sys.stdout = os.fdopen(1, 'w', 0) try: global fcntl, struct, termios import fcntl, struct, termios termios.TIOCGWINSZ except: try: curses.setupterm() curses.tigetnum('lines'), curses.tigetnum('cols') except: pass else: termsize = None, 2 else: termsize = None, 1 def gettermsize(): "Return the dynamic terminal geometry" global termsize if not termsize[0]: try: if termsize[1] == 1: s = struct.pack('HHHH', 0, 0, 0, 0) x = fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ, s) return struct.unpack('HHHH', x)[:2] elif termsize[1] == 2: curses.setupterm() return curses.tigetnum('lines'), curses.tigetnum('cols') else: termsize = (int(getenv('LINES')), int(getenv('COLUMNS'))) except: termsize = 25, 80 return termsize def gettermcolor(color=True): if color and sys.stdout.isatty(): try: import curses curses.setupterm() if curses.tigetnum('colors') < 0: return False except: info(1, 'Color support is disabled, python-curses is not installed.') return False return color def getcpunr(): "Return the number of CPUs in the system" cpunr = -1 for line in dopen('/proc/stat').readlines(): if line[0:3] == 'cpu': cpunr = cpunr + 1 if cpunr < 0: raise "Problem finding number of CPUs in system." return cpunr ### FIXME: Add scsi support too and improve def sysfs_dev(device): "Convert sysfs device names into device names" m = re.match('ide/host([0-9])/bus([0-9])/target([0-9])/lun([0-9])/disc', device) if m: l = m.groups() # ide/host0/bus0/target0/lun0/disc -> 0 -> hda # ide/host0/bus1/target0/lun0/disc -> 2 -> hdc nr = int(l[1]) * 2 + int(l[3]) return 'hd' + chr(ord('a') + nr) m = re.match('placeholder', device) if m: return 'sdX' return device def dev(maj, min): "Convert major/minor pairs into device names" ram = [1, ] ide = [3, 22, 33, 34, 56, 57, 88, 89, 90, 91] loop = [7, ] scsi = [8, 65, 66, 67, 68, 69, 70, 71, 128, 129, 130, 131, 132, 133, 134, 135] md = [9, ] ida = [72, 73, 74, 75, 76, 77, 78, 79] ubd = [98,] cciss = [104,] dm = [253,] if maj in scsi: disc = chr(ord('a') + scsi.index(maj) * 16 + min / 16) part = min % 16 if not part: return 'sd%s' % disc return 'sd%s%d' % (disc, part) elif maj in ide: disc = chr(ord('a') + ide.index(maj) * 2 + min / 64) part = min % 64 if not part: return 'hd%s' % disc return 'hd%s%d' % (disc, part) elif maj in dm: return 'dm-%d' % min elif maj in md: return 'md%d' % min elif maj in loop: return 'loop%d' % min elif maj in ram: return 'ram%d' % min elif maj in cciss: disc = cciss.index(maj) * 16 + min / 16 part = min % 16 if not part: return 'c0d%d' % disc return 'c0d%dp%d' % (disc, part) elif maj in ida: cont = ida.index(maj) disc = min / 16 part = min % 16 if not part: return 'ida%d-%d' % (cont, disc) return 'ida%d-%d-%d' % (cont, disc, part) elif maj in ubd: disc = ubd.index(maj) * 16 + min / 16 part = min % 16 if not part: return 'ubd%d' % disc return 'ubd%d-%d' % (disc, part) else: return 'dev%d-%d' % (maj, min) #def mountpoint(dev): # "Return the mountpoint of a mounted device/file" # for entry in dopen('/etc/mtab').readlines(): # if entry: # list = string.split(entry) # if dev == list[0]: # return list[1] def readfile(file): ret = '' for line in open(file,'r').readlines(): ret = ret + line return ret def signaler(signum, frame): signal.alarm(interval) def exit(ret): signal.signal(signal.SIGALRM, signal.SIG_IGN) sys.stdout.write(ansi['reset']) sys.exit(ret) def listmodules(): import glob rows, cols = gettermsize() remod = re.compile('.+/dstat_(.+).py$') for path in sys.path: list = [] for file in glob.glob(path + '/dstat_*.py'): list.append(remod.match(file).groups()[0]) if not list: continue list.sort() cols2 = cols - 8 print '%s:\n\t' % os.path.abspath(path), for mod in list: cols2 = cols2 - len(mod) - 2 if cols2 <= 0: print '\n\t', cols2 = cols - len(mod) - 10 print mod + ',', print def main(): global update, loop, step, pagesize, cpunr, ansi, interval, outputfile, tick, cols loop = update = 0 step = op.delay tick = ticks() pagesize = resource.getpagesize() cpunr = getcpunr() # hz = os.sysconf('SC_CLK_TCK') interval = 1 user = getpass.getuser() hostname = string.split(os.uname()[1], '.')[0] rows, cols = gettermsize() ### Write term-title if sys.stdout.isatty(): shell = getenv('XTERM_SHELL') term = getenv('TERM') if shell == '/bin/bash' and term and re.compile('(screen*|xterm*)').match(term): sys.stdout.write('\033]0;(%s@%s) %s %s\007' % (user, hostname, os.path.basename(sys.argv[0]), string.join(op.args, ' '))) ### Check terminal capabilities op.color = gettermcolor(op.color) if op.output: if os.path.exists(op.output): outputfile = open(op.output, 'a', 0) outputfile.write('\n\n') else: outputfile = open(op.output, 'w', 0) outputfile.write('"Dstat %s CSV output"\n' % VERSION) outputfile.write('"Author:","Dag Wieers ",,,,"URL:","http://dag.wieers.com/home-made/dstat/"\n') outputfile.write('"Host:","%s",,,,"User:","%s"\n' % (hostname, user)) outputfile.write('"Cmdline:","dstat %s",,,,"Date:","%s"\n\n' % (' '.join(op.args), time.strftime('%d %b %Y %H:%M:%S %Z', time.localtime()))) ### Empty ansi database if no colors are requested if not op.color: op.update = False for key in ansi.keys(): ansi[key] = '' if not op.update: interval = op.delay ### Build list of requested modules linewidth = 0 oldvislist = [] totlist = [] for module in op.modlist: if module in ('list', 'help'): listmodules() exit(0) elif module == 'cpu': mods = ( 'cpu', 'cpu24' ) elif module == 'disk': mods = ( 'disk', 'disk24', 'disk24old' ) elif module == 'int': mods = ( 'int', 'int24' ) elif module == 'page': mods = ( 'page', 'page24' ) elif module == 'swap': mods = ( 'swap', 'swapold' ) else: mods = ( module, ) for mod in mods: try: if 'dstat_'+mod not in globals().keys(): import imp file, pathname, description = imp.find_module('dstat_'+mod) ### Try loading python plugin if description[0] == '.py': # exec compile(readfile(pathname), pathname, 'exec') execfile(pathname) exec 'o = dstat_%s()' % mod ### Try loading C plugin elif description[0] == '.so': exec 'import dstat_%s' % mod exec 'o = dstat_%s.new()' % mod # exec 'o = dstat_%s.init(dstat)' % mod # print dir(o) # print o.__module__ # print o.name else: info(1, 'Module is of unknown type.') ### Remove defect stat objects and calculate line length if not o.check(): raise Exception, 'Unknown problem, please report' # except Exception, e: # if mod == mods[-1]: # info(1, 'Module %s has problems. (%s)' % (mod, e)) # if op.debug: # raise # continue else: exec 'o = dstat_%s()' % mod # print o.__module__ except Exception, e: if mod == mods[-1]: info(1, 'Module %s failed to load. (%s)' % (mod, e)) elif op.debug: info(1, 'Module %s failed to load, trying another. (%s)' % (mod, e)) if op.debug: raise # tb = sys.exc_info()[2] continue linewidth = linewidth + o.statwidth() + 1 totlist.append(o) break if not totlist: die(8, 'None of the stats you selected are available.') if op.debug: for o in totlist: print 'Module', string.split(str(o.__class__), '.')[1], if hasattr(o, 'file'): print 'requires', o.file, print if op.output: showcsvtitle(1, totlist) showcsvtitle(2, totlist) ### Increase precision if we're root (does not seem to have effect) # if os.geteuid() == 0: # os.nice(-20) # sys.setcheckinterval(op.delay / 10000) ### Always show header the first time showheader = True signal.signal(signal.SIGALRM, signaler) signal.alarm(interval) tt = 0 ### Let the games begin while update <= op.delay * op.count or op.count == -1: if op.debug: if step == 1: tt = 0 t1 = time.time() curwidth = 8 else: curwidth = 0 ### Trim object list to what is visible on screen if sys.stdout.isatty(): rows, cols = gettermsize() vislist = [] for o in totlist: newwidth = curwidth + o.statwidth() + 1 if newwidth <= cols or ( vislist == totlist[:-1] and newwidth < cols ): vislist.append(o) curwidth = newwidth ### Check when to display the header if op.header and rows >= 6: if oldvislist != vislist: showheader = True elif step == 1 and loop % (rows - 1) == 0: showheader = True oldvislist = vislist else: vislist = totlist if showheader: if loop == 0 and totlist != vislist: info(1, 'Terminal width too small, trimming output.') showheader = False showtitle(1, totlist, vislist, ansi['darkblue'] + char['space'], ansi['darkblue'] + char['gt']) showtitle(2, totlist, vislist, ansi['gray'] + char['pipe'], ansi['darkblue'] + char['gt']) ### Prepare the colors for intermediate updates, last step in a loop is definitive if step == op.delay: ansi['default'] = ansi['reset'] else: ansi['default'] = ansi['gray'] line = ansi['default'] ### Calculate all objects (visible, invisible) oline = '' for o in totlist: o.extract() if o in vislist: line = line + o.show() + o.showend(totlist, vislist) if op.output and step == op.delay: oline = oline + o.showcsv() + o.showcsvend(totlist, vislist) ### Print stats sys.stdout.write(line + ansi['default']) if op.output and step == op.delay: outputfile.write(oline + '\n') ### Print debugging output if op.debug: t2 = time.time(); tt = tt + (t2 - t1) * 1000.0 if loop == 0: tt = tt * step if op.debug == 1: sys.stdout.write('%s%6.2fms%s' % (ansi['darkblue'], tt / step, ansi['default'])) elif op.debug > 1: sys.stdout.write('%s%6.2f %s%d:%d:%d%s' % (ansi['darkblue'], tt / step, ansi['gray'], loop, step, update, ansi['default'])) ### If intermediate results, update increases with 1 sec (=interval) update = update + interval if not op.update: sys.stdout.write('\n') ### Do not pause when this is the final loop if update <= op.delay * op.count or op.count == -1: signal.pause() ### The last step in a loop is to show the definitive line on its own line if op.update: if step == op.delay: sys.stdout.write('\n' + ansi['reset'] + ansi['clearline'] + ansi['save']) else: sys.stdout.write(ansi['restore']) loop = (update + op.delay - 1) / op.delay step = ((update - 1) % op.delay) + 1 tick = step signal.signal(signal.SIGALRM, signal.SIG_IGN) ### Main entrance if __name__ == '__main__': try: initterm() op = Options(sys.argv[1:]) main() except KeyboardInterrupt, e: print ansi['default'] # except IOError, e: # if e.errno != 32: ## [Errno 32] Broken pipe # print # print 'IOError: %s' % e # exit(7) # except OSError, e: # print # print 'OSError: %s' % e # exit(7) exit(0) else: op = Options('') step = 1 tick = ticks() # vim:ts=4:sw=4:et