#!/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, os, time, sched, re import types, 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 < (2, 2): sys.exit('error: Python 2.2 or later required, try dstat15 instead') ### Workaround for python <= 2.2.1 try: True, False except NameError: True = 1 False = 0 ### Workaround for python < 2.3 #if 'enumerate' not in __builtins__.__dict__.keys(): if sys.version_info < (2, 3) and sys.version_info >= (2, 2): def enumerate(sequence): index = 0 for item in sequence: yield index, item index = index + 1 elif sys.version_info < (2, 2): 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 'sum' not in __builtins__.__dict__.keys(): if sys.version_info < (2, 3): 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 self.pidfile = 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=', 'pidfile=', '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 = arg.split(',') elif opt in ['-d', '--disk']: self.modlist.append('disk') elif opt in ['-D']: self.disklist = arg.split(',') 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 = arg.split(',') 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 + arg.split(',') elif opt in ['-n', '--net']: self.modlist.append('net') elif opt in ['-N']: self.netlist = arg.split(',') 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 = arg.split(',') 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 ['--pidfile']: self.pidfile = 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)' % (os.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): for fd in self.fd: fd.seek(0) for line in fd.readlines(): yield line 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 + self.name[0:max].center(max).replace(' ', '-') + ansi['default'] for i, name in enumerate(self.name): max = self.width() ret = ret + name[0:max].center(max).replace(' ', '-') 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'] + nick.center(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'] + nick.center(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 = line.split() 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 = line.split() 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): if sum(self.cn2[name]) > sum(self.cn1[name]): self.val[name][i] = 100.0 * (self.cn2[name][i] - self.cn1[name][i]) / (sum(self.cn2[name]) - sum(self.cn1[name])) else: print >>sys.stderr, "Error: tick problem detected, this should never happen !" 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 = line.split() 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 = line.split() 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 = ['dsk/'+name for name in self.vars] self.init(self.vars + ['total',], 2) def discover(self, *list): ret = [] for line in self.readlines(): l = line.split() 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 = line.split() 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 = ['dsk/'+sysfs_dev(name) for name in self.vars] self.init(self.vars + ['total',], 2) def discover(self, *list): ret = [] for line in self.readlines(): l = line.split() 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 = line.split() 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 = ['dsk/'+name for name in self.vars] self.init(self.vars + ['total',], 2) def discover(self, *list): ret = [] for line in self.readlines(): l = line.split(':') if len(l) < 3: continue name = l[0] if name != 'disk_io': continue for pair in line.split()[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 = line.split(':') if len(l) < 3: continue name = l[0] if name != 'disk_io': continue for pair in line.split()[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) ### We are now using the starttime instead of the execution time of this plugin def extract(self): # self.val['epoch'] = time.time() self.val['epoch'] = starttime # def show(self): # return ansi['reset'] + ( '%10.2f' % self.val['epoch'] ) 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 = line.split() if len(l) <= cpunr: continue l1 = l[0].split(':')[0] l2 = ' '.join(l[cpunr+2:]).split(',') ret[l1] = l1 for name in l2: ret[name.strip().lower()] = l1 return ret def discover(self): ret = [] for line in self.readlines(): l = line.split() 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 = line.split() # if l[0] != 'intr': 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', '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 + ['total',]: ret.append(name) elif name.lower() in self.intmap.keys(): ret.append(self.intmap[name.lower()]) return ret def extract(self): for line in self.readlines(): l = line.split() if not l or l[0] != 'intr': continue for name in self.vars: if name != 'total': self.cn2[name] = long(l[int(name) + 2]) self.cn2['total'] = 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_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 = line.split() if len(l) <= cpunr: continue l1 = l[0].split(':')[0] l2 = ' '.join(l[cpunr+2:]).split(',') ret[l1] = l1 for name in l2: ret[name.strip().lower()] = l1 return ret def discover(self): ret = [] for line in self.readlines(): l = line.split() if len(l) < cpunr+1: continue name = l[0].split(':')[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 = line.split() # 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 name.lower() in self.intmap.keys(): ret.append(self.intmap[name.lower()]) return ret def extract(self): for line in self.readlines(): l = line.split() if len(l) < cpunr+1: continue name = l[0].split(':')[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 = line.split() 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 = line.split() 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 = line.split() if len(l) < 2: continue name = l[0].split(':')[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 = ['net/'+name for name in self.vars] self.init(self.vars + ['total',], 2) def discover(self, *list): ret = [] for line in self.readlines(): l = line.replace(':', ' ').split() 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 = line.replace(':', ' ').split() 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 = line.split() 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 = line.split() 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 = line.split() 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): lines = -1 for line in self.readlines(): lines = lines + 1 self.val['sockets'] = lines ### Cannot use len() on generator # 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 = ['swp/'+improve(name) for name in self.vars] self.init(self.vars + ['total',], 2) def discover(self, *list): ret = [] for line in self.readlines(): l = line.split() 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 = line.split() 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 = line.split() if len(l) < 2: continue name = l[0].split(':')[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 = line.split() 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 = line.split() 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 ### FIXME: If timefmt < len(self.nick) output is fucked up class dstat_time(dstat): def __init__(self): self.name = 'time' self.timefmt = os.getenv('DSTAT_TIMEFMT') if self.timefmt: tmp = time.strftime(self.timefmt, time.localtime()) self.format = ('s', len(tmp), 0) elif op.debug: self.format = ('s', 18, 0) else: self.format = ('s', 14, 0) self.nick = ('date/time',) self.vars = ('time',) self.init(self.vars, 1) ### We are now using the starttime for this plugin, not the execution time of this plugin def extract(self): if self.timefmt: self.val['time'] = time.strftime(self.timefmt, time.localtime(starttime)) elif op.debug: self.val['time'] = time.strftime('%d-%m %H:%M:%S', time.localtime(starttime)) + ".%03d" % (round(starttime * 1000 % 1000 )) else: self.val['time'] = time.strftime('%d-%m %H:%M:%S', time.localtime(starttime)) 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 = line.split() 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 = line.split() 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 = line.split() if len(l) < 2: continue return float(l[0]) except: for line in dopen('/proc/stat').readlines(): l = line.split() 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 = str.split('/')[3] elif str.startswith('/dev/'): str = str.split('/')[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 ret.split('\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(long(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(long(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['white'] + ansi['redbg'] + '-'.rjust(max) + ' ' + ansi['default'] else: return ansi['white'] + ansi['redbg'] + '-'.rjust(max) + ansi['default'] 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(round(var)/mp)%len(colors)] elif type in ('f'): color = colors[c%len(colors)] else: color = 'default' if type in ('s',): ret = ansi['default'] + ret.ljust(max) else: ret = ansi[color] + ret.rjust(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 >>sys.stderr, str def die(ret, str): "Print error and exit with errorcode" print >>sys.stderr, str exit(ret) def initterm(): "Initialise terminal" global termsize ### 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] and not termsize[1]: 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(os.environ['LINES']), int(os.environ['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: print >>sys.stderr, '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 = entry.split() # if dev == list[0]: # return list[1] def readfile(file): ret = '' for line in open(file,'r').readlines(): ret = ret + line return ret #cdef extern from "sched.h": # struct sched_param: # int sched_priority # int sched_setscheduler(int pid, int policy,sched_param *p) # #SCHED_FIFO = 1 # #def switchRTCPriority(nb): # cdef sched_param sp # sp.sched_priority = nb # sched_setscheduler (0,SCHED_FIFO , &sp); def exit(ret): 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 pagesize, cpunr, ansi, outputfile global totlist, inittime global update, missed pagesize = resource.getpagesize() cpunr = getcpunr() interval = 1 user = getpass.getuser() hostname = os.uname()[1].split('.')[0] ### Write term-title if sys.stdout.isatty(): shell = os.getenv('XTERM_SHELL') term = os.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]), ' '.join(op.args))) ### Check background color (rxvt) ### COLORFGBG="15;default;0" # if os.environ['COLORFGBG'] and len(os.environ['COLORFGBG'].split(';')) >= 3: # l = os.environ['COLORFGBG'].split(';') # bg = int(l[2]) # if bg < 7: # print 'Background is dark' # else: # print 'Background is light' # else: # print 'Background is unknown, assuming dark.' ### Check terminal capabilities op.color = gettermcolor(op.color) ### Prepare CSV output file 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()))) ### Create pidfile if op.pidfile: try: pidfile = open(op.pidfile, 'w', 0) pidfile.write(str(os.getpid())) pidfile.close() except Exception, e: print >>sys.stderr, 'Failed to create pidfile %s' % op.pidfile, e op.pidfile = False ### Empty ansi database if no colors are requested if not op.color: op.update = False for key in ansi.keys(): ansi[key] = '' # print ansi['blackbg'] if not op.update: interval = op.delay ### Build list of requested modules linewidth = 0 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: print >>sys.stderr, 'Module %s is of unknown type.' % mod ### Remove defect stat objects and calculate line length if not o.check(): raise Exception, 'Unknown problem, please report' else: exec 'o = dstat_%s()' % mod # print o.__module__ except Exception, e: if mod == mods[-1]: print >>sys.stderr, 'Module %s failed to load. (%s)' % (mod, e) elif op.debug: print >>sys.stderr, 'Module %s failed to load, trying another. (%s)' % (mod, e) if op.debug >= 3: 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', str(o.__class__).split('.')[1], if hasattr(o, 'file'): print 'requires', o.file, print if op.output: showcsvtitle(1, totlist) showcsvtitle(2, totlist) scheduler = sched.scheduler(time.time, time.sleep) inittime = time.time() update = 0 missed = 0 ### Let the games begin while update <= op.delay * op.count or op.count == -1: scheduler.enterabs(inittime + update, 1, perform, (update,)) # scheduler.enter(1, 1, perform, (update,)) scheduler.run() update = update + interval if op.update: sys.stdout.write('\n') def perform(update): global totlist, oldvislist, vislist, showheader, rows, cols global tick, totaltime, starttime global loop, step, missed starttime = time.time() loop = (update - 1 + op.delay) / op.delay step = ((update - 1) % op.delay) + 1 ### Get current time (may be different from schedule) for debugging if not op.debug: curwidth = 0 else: if step == 1 or loop == 0: totaltime = 0 curwidth = 8 ### FIXME: This is temporary functionality, we should do this better ### If it takes longer than 500ms, than warn ! if loop != 0 and starttime - inittime - update > 1: missed = missed + 1 return 0 ### Initialise certain variables if loop == 0: tick = ticks() rows, cols = 0, 0 vislist = [] oldvislist = [] showheader = True else: tick = step ### FIXME: Make this part smarter if sys.stdout.isatty(): oldrows, oldcols = rows, cols rows, cols = gettermsize() ### Trim object list to what is visible on screen if oldcols != cols: 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 ### The first step is to show the definitive line if necessary if op.update: if step == 1 and update != 0: sys.stdout.write('\n' + ansi['reset'] + ansi['clearline'] + ansi['save']) elif loop != 0: sys.stdout.write(ansi['restore']) ### Display header if showheader: if loop == 0 and totlist != vislist: print >>sys.stderr, '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'] ### Calculate all objects (visible, invisible) line = '' 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) if op.output and step == op.delay: outputfile.write(oline + '\n') ### Print debugging output if op.debug: totaltime = totaltime + (time.time() - starttime) * 1000.0 if loop == 0: totaltime = totaltime * step if op.debug == 1: sys.stdout.write('%s%6.2fms%s' % (ansi['darkblue'], totaltime / step, ansi['default'])) elif op.debug == 2: sys.stdout.write('%s%6.2f %s%d:%d%s' % (ansi['darkblue'], totaltime / step, ansi['darkred'], loop, step, ansi['default'])) elif op.debug > 2: sys.stdout.write('%s%6.2f %s%d:%d:%d%s' % (ansi['darkblue'], totaltime / step, ansi['darkred'], loop, step, update, ansi['default'])) if missed > 0: # sys.stdout.write(' '+ansi['redbg']+ansi['white']+'= warn =') sys.stdout.write(' '+ansi['redbg']+ansi['white']+'missed '+str(missed+1)+' ticks') missed = 0 ### Finish the line if not op.update: sys.stdout.write('\n') sys.stdout.flush() ### Main entrance if __name__ == '__main__': try: initterm() op = Options(sys.argv[1:]) main() except KeyboardInterrupt, e: print ansi['default'] if op.pidfile and os.path.exists(op.pidfile): os.remove(op.pidfile) exit(0) else: op = Options('') step = 1 # vim:ts=4:sw=4:et