├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .travis.yml ├── AUTHORS ├── COPYING ├── ChangeLog ├── LINKS ├── Makefile ├── README.adoc ├── TODO ├── docs ├── Makefile ├── counter-rollovers.adoc ├── counter-rollovers.html ├── cplugins.adoc ├── cplugins.html ├── dstat-paper.adoc ├── dstat-paper.html ├── dstat.1 ├── dstat.1.adoc ├── dstat.1.html ├── examples.adoc ├── examples.html ├── performance.adoc ├── performance.html ├── screen.adoc └── screen.html ├── dstat ├── dstat.conf ├── examples ├── curstest ├── devtest.py ├── dstat.py ├── mmpipe.py ├── mstat.py ├── read.py └── tdbtest ├── packaging ├── rpm │ └── dstat.spec └── snap │ ├── python2 │ └── snapcraft.yaml ├── plugins ├── dstat_battery.py ├── dstat_battery_remain.py ├── dstat_condor_queue.py ├── dstat_cpufreq.py ├── dstat_dbus.py ├── dstat_disk_avgqu.py ├── dstat_disk_avgrq.py ├── dstat_disk_svctm.py ├── dstat_disk_tps.py ├── dstat_disk_util.py ├── dstat_disk_wait.py ├── dstat_dstat.py ├── dstat_dstat_cpu.py ├── dstat_dstat_ctxt.py ├── dstat_dstat_mem.py ├── dstat_fan.py ├── dstat_freespace.py ├── dstat_fuse.py ├── dstat_gpfs.py ├── dstat_gpfs_ops.py ├── dstat_helloworld.py ├── dstat_ib.py ├── dstat_innodb_buffer.py ├── dstat_innodb_io.py ├── dstat_innodb_ops.py ├── dstat_jvm_full.py ├── dstat_jvm_vm.py ├── dstat_lustre.py ├── dstat_md_status.py ├── dstat_memcache_hits.py ├── dstat_mongodb_conn.py ├── dstat_mongodb_mem.py ├── dstat_mongodb_opcount.py ├── dstat_mongodb_queue.py ├── dstat_mongodb_stats.py ├── dstat_mysql5_cmds.py ├── dstat_mysql5_conn.py ├── dstat_mysql5_innodb.py ├── dstat_mysql5_innodb_basic.py ├── dstat_mysql5_innodb_extra.py ├── dstat_mysql5_io.py ├── dstat_mysql5_keys.py ├── dstat_mysql_io.py ├── dstat_mysql_keys.py ├── dstat_net_packets.py ├── dstat_nfs3.py ├── dstat_nfs3_ops.py ├── dstat_nfsd3.py ├── dstat_nfsd3_ops.py ├── dstat_nfsd4_ops.py ├── dstat_nfsstat4.py ├── dstat_ntp.py ├── dstat_postfix.py ├── dstat_power.py ├── dstat_proc_count.py ├── dstat_qmail.py ├── dstat_redis.py ├── dstat_rpc.py ├── dstat_rpcd.py ├── dstat_sendmail.py ├── dstat_snmp_cpu.py ├── dstat_snmp_load.py ├── dstat_snmp_mem.py ├── dstat_snmp_net.py ├── dstat_snmp_net_err.py ├── dstat_snmp_sys.py ├── dstat_snooze.py ├── dstat_squid.py ├── dstat_test.py ├── dstat_thermal.py ├── dstat_top_bio.py ├── dstat_top_bio_adv.py ├── dstat_top_childwait.py ├── dstat_top_cpu.py ├── dstat_top_cpu_adv.py ├── dstat_top_cputime.py ├── dstat_top_cputime_avg.py ├── dstat_top_int.py ├── dstat_top_io.py ├── dstat_top_io_adv.py ├── dstat_top_latency.py ├── dstat_top_latency_avg.py ├── dstat_top_mem.py ├── dstat_top_oom.py ├── dstat_utmp.py ├── dstat_vm_cpu.py ├── dstat_vm_mem.py ├── dstat_vm_mem_adv.py ├── dstat_vmk_hba.py ├── dstat_vmk_int.py ├── dstat_vmk_nic.py ├── dstat_vz_cpu.py ├── dstat_vz_io.py ├── dstat_vz_ubc.py ├── dstat_wifi.py ├── dstat_zfs_arc.py ├── dstat_zfs_l2arc.py └── dstat_zfs_zil.py └── proc ├── diskstats-2.6.11 ├── partitions-2.4.21 ├── partitions-2.4.24 ├── partitions-2.6.11 ├── stat-2.4.21 ├── stat-2.4.24 └── stat-2.6.11 /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ##### SUMMARY 4 | 5 | 6 | ##### ISSUE TYPE 7 | 8 | - Bug Report 9 | - Feature Idea 10 | - Documentation Report 11 | 12 | ##### DSTAT VERSION 13 | ``` 14 | 15 | ``` 16 | 17 | 27 | 28 | ##### OS / ENVIRONMENT 29 | 33 | 34 | ##### STEPS TO REPRODUCE 35 | 39 | 40 | ``` 41 | 42 | ``` 43 | 44 | 45 | 46 | ##### EXPECTED RESULTS 47 | 48 | 49 | ##### ACTUAL RESULTS 50 | 51 | 52 | ``` 53 | 54 | ``` 55 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ##### ISSUE TYPE 2 | 3 | - New plugin pull-request 4 | - Feature pull-request 5 | - Bugfix pull-request 6 | - Docs pull-request 7 | 8 | ##### DSTAT VERSION 9 | ``` 10 | 11 | ``` 12 | 13 | ##### SUMMARY 14 | 15 | 16 | 21 | 22 | ``` 23 | 24 | ``` 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: python 3 | python: 4 | - "2.6" 5 | - "2.7" 6 | - "3.5" 7 | - "3.6" 8 | #install: 9 | # - pip install dbus-python 10 | # - pip install python-utmp 11 | script: 12 | - python ./dstat --version 13 | - python ./dstat -taf 1 5 14 | - python ./dstat -t --all-plugins 1 5 15 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Dag Wieers 2 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dstat-real/dstat/e428c7dc7137f81f8ca6bef8854b37a4ddd4f337/ChangeLog -------------------------------------------------------------------------------- /LINKS: -------------------------------------------------------------------------------- 1 | Terminal emulation 2 | http://www.termsys.demon.co.uk/vtansi.htm 3 | http://vt100.net/docs/vt100-ug/chapter3.html#DECSC 4 | 5 | NFS 6 | http://www.hn.edu.cn/book/NetWork/NetworkingBookshelf_2ndEd/nfs/ch14_02.htm 7 | 8 | MySQL performance counters 9 | http://www.mysql.com/news-and-events/newsletter/2004-01/a0000000301.html 10 | 11 | Kernel schedstat 12 | http://eaglet.rain.com/rick/linux/schedstat/v10/format-10.html 13 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | name = dstat 2 | version = $(shell awk '/^Version: / {print $$2}' $(name).spec) 3 | 4 | prefix = /usr 5 | sysconfdir = /etc 6 | bindir = $(prefix)/bin 7 | datadir = $(prefix)/share 8 | mandir = $(datadir)/man 9 | 10 | .PHONY: all install docs clean 11 | 12 | all: docs 13 | @echo "Nothing to be build." 14 | 15 | docs: 16 | $(MAKE) -C docs docs 17 | 18 | install: 19 | # -[ ! -f $(DESTDIR)$(sysconfdir)/dstat.conf ] && install -D -m0644 dstat.conf $(DESTDIR)$(sysconfdir)/dstat.conf 20 | install -Dp -m0755 dstat $(DESTDIR)$(bindir)/dstat 21 | install -d -m0755 $(DESTDIR)$(datadir)/dstat/ 22 | install -Dp -m0755 dstat $(DESTDIR)$(datadir)/dstat/dstat.py 23 | install -Dp -m0644 plugins/dstat_*.py $(DESTDIR)$(datadir)/dstat/ 24 | # install -d -m0755 $(DESTDIR)$(datadir)/dstat/examples/ 25 | # install -Dp -m0755 examples/*.py $(DESTDIR)$(datadir)/dstat/examples/ 26 | install -Dp -m0644 docs/dstat.1 $(DESTDIR)$(mandir)/man1/dstat.1 27 | 28 | docs-install: 29 | $(MAKE) -C docs install 30 | 31 | clean: 32 | rm -f examples/*.pyc plugins/*.pyc 33 | $(MAKE) -C docs clean 34 | 35 | test: 36 | ./dstat --version 37 | ./dstat -taf 1 5 38 | ./dstat -t --all-plugins 1 5 39 | 40 | dist: clean 41 | $(MAKE) -C docs dist 42 | # svn up && svn list -R | pax -d -w -x ustar -s ,^,$(name)-$(version)/, | bzip2 >../$(name)-$(version).tar.bz2 43 | # svn st -v --xml | \ 44 | xmlstarlet sel -t -m "/status/target/entry" -s A:T:U '@path' -i "wc-status[@revision]" -v "@path" -n | \ 45 | pax -d -w -x ustar -s ,^,$(name)-$(version)/, | \ 46 | bzip2 >../$(name)-$(version).tar.bz2 47 | git ls-files | pax -d -w -x ustar -s ,^,$(name)-$(version)/, | bzip2 >../$(name)-$(version).tar.bz2 48 | 49 | rpm: dist 50 | rpmbuild -tb --clean --rmspec --define "_rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm" --define "_rpmdir ../" ../$(name)-$(version).tar.bz2 51 | 52 | srpm: dist 53 | rpmbuild -ts --clean --rmspec --define "_rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm" --define "_srcrpmdir ../" ../$(name)-$(version).tar.bz2 54 | 55 | snap: 56 | cd packaging/snap/; snapcraft 57 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | = DSTAT 2 | 3 | image:https://travis-ci.org/rear/rear.svg?branch=master["Build Status", link="https://travis-ci.org/dagwieers/dstat"] 4 | 5 | === DSTAT development has been terminated 6 | 7 | Due to actions taken by RedHat to https://github.com/dstat-real/dstat/issues/170[hijack] the DSTAT name, further development of this project has ceased. Development of this project is taking place on the https://github.com/scottchiefbaker/dool[Dool] fork. 8 | 9 | So long and thanks for all the fish 10 | 11 | === Information 12 | 13 | Dstat is a versatile replacement for vmstat, iostat, mpstat, netstat and ifstat. Dstat overcomes some of their limitations and while also adding extra features, more counters and flexibility. Dstat is handy for monitoring systems during performance tuning tests, benchmarks or troubleshooting. 14 | 15 | Dstat allows you to view all of your system resources instantly. For example you can compare disk usage in combination with interrupts from your IDE controller, or compare network bandwidth directly with disk throughput. 16 | 17 | Dstat gives you detailed selective information in columns and clearly indicates the magnitude and unit the output is displayed. Less confusion, less mistakes. 18 | 19 | Dstat is unique in letting you aggregate block device throughput for a certain diskset or networkset. This allows you to see the throughput for all the block devices that make up a single filesystem or storage system. 20 | 21 | Dstat is extensible! You can write your own Dstat plugins to monitor whatever you like in just a few minutes based on provided examples and a little bit of Python knowledge. 22 | 23 | Dstat's output by default is designed for human consumption in real-time. Dstat also allows CSV output so you to archive historical data in a file to be imported later into a spreadsheet. This is useful for generating graphs. 24 | 25 | Since it's practically impossible to test dstat on every possible permutation of kernel, python or distribution version, we need your help and feedback in testing. 26 | 27 | If you have improvements or bug reports, please send them to: 28 | 29 | http://github.com/dagwieers/dstat 30 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | ### Disclaimer 2 | This is my TODO list. If you're interested in one of these features, there 3 | are 2 options. Either wait for someone to implement it or sponsor someone 4 | to implement it. 5 | 6 | If you want to implement something, please contact me first so that we can 7 | discuss acceptable implementations. In some cases I haven't thought about 8 | it too deeply, but for others I know exactly what I require. 9 | 10 | If you have other nice ideas that you think would be an improvement, please 11 | contact me as well. :) Send an email to: Dag Wieers 12 | 13 | ### Usability 14 | + Add --config option and use /etc/dstat.conf and ~/.dstat to influence output (see example dstat.conf) 15 | + Allow to force to given magnitude (--unit=kilo) 16 | + Look at possibilities to show deviation (on second line ? not practical) 17 | + Check for dark/light background color and change colors accordingly (option --bw/--blackonwhite) 18 | + Show parts of counters in other colors (eg. color the 6 in 6134B in yellow to indicate it's kilobyte) 19 | + Look into adding sched_setscheduler() calls for improved priority 20 | 21 | ### General improvements 22 | + Implement better (?) protection against counter rollovers 23 | (see mail from Sebastien Prud'homme/Ross Brattain, already improved in meantime) 24 | 25 | ### Documentation (help welcome!) 26 | + Document every plugin as part of python comments (explain unit, what it means etc...) 27 | + Create document on general system performance tuning 28 | (explaining the different values in /proc, especially the concerning ones) 29 | + Create document on general system performance tools 30 | (explaining the different uses of tools like dstat, iostat, pmap, strace, tcpdump) 31 | + Comply to PEP8: http://www.python.org/dev/peps/pep-0008/ 32 | 33 | ### Export/Graph 34 | + Interface with rrdtool (python-rrd ?) 35 | + Allow for different types of export modules (only CSV now) 36 | - ODS could include graphs for plugins ! 37 | - HTML output plugin helps for people sharing output on websites 38 | + Allow to write out to syslog (or remote syslog) 39 | + Allow to write buffered to disk (optional ?) 40 | + Write out user input to CSV 41 | 42 | ### Plugin improvements 43 | + Don't calculate counters twice when a plugin is loaded twice 44 | 45 | ### Extending statistics (help welcome!) 46 | + Add %steal, %guest to --cpu-adv plugin 47 | + Add slab plugin (see /proc/slabinfo and slabtop) 48 | + Add xorg plugin (xdpyinfo, xrestop) 49 | - Add 'most expensive X app' (look at xrestop) 50 | - Add number of (active) X sessions and X clients 51 | + Add icmp plugin ? 52 | + Add application plugin (-a or -A pid,cmd) 53 | + Add user plugin (number of users logged on, utmp is not that useful, /proc/key-users) 54 | + Look into interfacing with apps 55 | - amavisd, apache, bind, cifs, dhcpd, dnsmasq, gfs, samba, squid 56 | + Look into interfacing with specific HW counters in /proc 57 | - qla2300 58 | + Look at /proc/meminfo, /proc/mdstat, /proc/net/netstat, /proc/net/snmp, /proc/vmstat, /proc/drbd 59 | + Look at /proc/fs/cifs/stats 60 | + Add i2c plugin (see /sys/class/i2c-adapter/i2c-*/*/*/*/*/*) 61 | + Allow for SNMP counters to be added 62 | + Add LVM stats 63 | + Allow to have multiple '1st expensive ... app' and '2nd expensive ... app' 64 | + Add 'most iowaiting app' plugin 65 | + Add systemtap/perf integration 66 | + Add dropwatch statistics 67 | 68 | ### Plugin issues 69 | + plugins that use /proc/pid/stats are reasonably slow (implement in C might help) 70 | + disk plugin: /proc/partitions can have negative numbers, seen on systems with long uptime. dstat handles this except for calculating the very first stat, no work-around possible? 71 | + proc plugin: (run and blk) does not work on 2.4.24+ (to be confirmed ?) 72 | + tcp plugin: is very slow and generates lots of softirqs (on busy systems), to be confirmed 73 | 74 | ### Redesign (v1.0) 75 | + Create a nicer interface for plugins (with meaningful names, eg. not nick) 76 | 77 | ### Redesign (v2.0) 78 | + Create modules that can contain samples of different units 79 | 80 | CPU: (see mpstat) 81 | sys, usr, idl, iow, hiq, siq (percentage) 82 | intr/sec (int) 83 | 84 | IO: (see iostat -x) 85 | tps (int) 86 | blk_read/sec, blk_wrtn/sec (kB/sec) 87 | 88 | + Design proper object model and namespace for _all_ possible stats 89 | + Create a seperate curses-based tool, much like nmon (dstat stays line-based) 90 | + Create client/server monitoring tool 91 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | prefix = /usr 2 | datadir = $(prefix)/share 3 | mandir = $(datadir)/man 4 | 5 | template = dagit.ott 6 | 7 | adoctargets = $(shell echo *.adoc) 8 | htmltargets = $(patsubst %.adoc, %.html, $(adoctargets)) 9 | 10 | all: 11 | 12 | dist: docs 13 | 14 | docs: dstat.1 $(htmltargets) 15 | 16 | install: dstat.1 17 | install -Dp -m0644 dstat.1 $(DESTDIR)$(mandir)/man1/dstat.1 18 | 19 | clean: 20 | rm -f dstat.1 *.html *.xml 21 | 22 | %.1.html: %.1.adoc 23 | asciidoc -d manpage $< 24 | 25 | %.html: %.adoc 26 | asciidoc $< 27 | 28 | %.1.xml: %.1.adoc 29 | asciidoc -b docbook -d manpage $< 30 | 31 | %.1: %.1.xml 32 | @xmlto man $< 33 | 34 | %.xml: %.adoc 35 | asciidoc -b docbook -d article -o $@ $< 36 | 37 | %.htm: %.adoc 38 | asciidoc -s -b html4 -d article -o $@ $< 39 | 40 | %.xhtml: %.adoc 41 | asciidoc -s -b xhtml11 -d article -o $@ $< 42 | 43 | %.tmp.odt: %.xml 44 | #»··-make -C /home/dag/home-made/docbook2odf/ dag-cv 45 | docbook2odf -f --params generate.meta=0 -o $@ $< 46 | 47 | %.odt: $(template) %.tmp.odt 48 | unoconv -f odt -t $(template) -o $@ $< 49 | -------------------------------------------------------------------------------- /docs/counter-rollovers.adoc: -------------------------------------------------------------------------------- 1 | = All you ever wanted to know about counter-rollovers in Dstat 2 | 3 | == What you need to know about counter rollovers 4 | Unfortunately, Dstat is susceptible for counter rollovers, which may give 5 | you bogus performance output. Linux currently implements counters as 32bit 6 | values (not sure on 64bit platforms). This means a counter can go up to 7 | 2^32 (= 4294967296 = 4G) values. 8 | 9 | Especially for network devices (which are calculated in bytes) this is too 10 | much as it means every 4GB, the counter is reset to 0. On a 1Gbps interface 11 | that is fully used, this happens every 32 seconds. On 2 bonded 10Gbps 12 | interfaces, this happens after 1.6 seconds. 13 | 14 | Since /proc is updated every second, this becomes almost impossible to catch. 15 | 16 | 17 | == How does this impact Dstat ? 18 | Currently Dstat has a problem if you specify delays that are too big. I.e. 19 | using 60 or 120 seconds delay in Dstat will make Dstat check these counters 20 | only once per minute or every two minutes. 21 | 22 | In case a counter rolls over, it may be lower than the previous value, or 23 | worse, the value may actually be higher. In the first case we compensate 24 | because we now a rollover (at least one) happened, but if in the interval more 25 | than one rollover happened, you're screwed. If however the rollover causes 26 | a higher value than the previous in that interval, you're screwed too :-) 27 | 28 | In both situations that you're screwed, we cannot help you either because we 29 | don't know. So we cannot compensate or even notify the user of the problem. 30 | This is very problematic, and it's important you are aware of this. 31 | 32 | 33 | == What are the solutions ? 34 | The only fix for Dstat is to check more often than the specified delay. 35 | Unfortunately, this requires a re-design (or an ugly hack). But if rollovers 36 | happen more than once (or values are larger than the max value) we cannot fix 37 | this. 38 | 39 | There are plans to use 64bit counters on Linux and/or changing the output from 40 | using bytes to kbytes. None of this is sure. (add pointers to threads) 41 | 42 | 43 | == What can I do ? 44 | Since this is Open Source, you are free to fix this and send me the fix. Or 45 | help with a redesign of Dstat to overcome this problem. Also look at the 46 | TODO file to see what other changes are expected in a redesign of Dstat. 47 | 48 | Since I have a lot of other responsibilities and am currently not using Dstat 49 | for something where this problem matters much, I will have no time to look at 50 | it closely (unless the fix or the redesign is made fairly simple). It all 51 | depends on how quick I think I can fix/redesign it and how much time I have. 52 | 53 | Your help could be to reduce the time it takes for me to fix it :) 54 | 55 | 56 | NOTE: Please send me improvements to this document. 57 | -------------------------------------------------------------------------------- /docs/cplugins.adoc: -------------------------------------------------------------------------------- 1 | Defining Python class methods in C 2 | http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/54352 3 | 4 | python class in c / global calls 5 | http://mail.python.org/pipermail/python-list/2002-August/160784.html 6 | 7 | How to create a python class in C 8 | http://mail.python.org/pipermail/python-list/2000-August/047158.html 9 | -------------------------------------------------------------------------------- /docs/examples.adoc: -------------------------------------------------------------------------------- 1 | = Dstat examples 2 | 3 | I've written a few examples that make use of the Dstat classes. 4 | 5 | The following examples currently exist: 6 | 7 | read.py - shows how to access dstat data 8 | mstat.py - small sub-second ministat tool 9 | 10 | Please send other examples or tools that make use of Dstat classes 11 | or changes to extend the current infrastructure. 12 | 13 | I'm not particularly happy with the current interface to Dstat, 14 | so any hints on how to improve it are welcome. Also look at the 15 | TODO for future changes. 16 | 17 | 18 | NOTE: Please send me improvements to this document. 19 | -------------------------------------------------------------------------------- /docs/performance.adoc: -------------------------------------------------------------------------------- 1 | = Dstat performance 2 | 3 | == Introduction 4 | Since Dstat is written in python, it is not optimized for performance. 5 | But that doesn't mean that Dstat performs bad, it performs quite good 6 | given its written in python and a lot of dedication went into profiling 7 | and optimizing Dstat and Dstat plugins. 8 | 9 | But when doing performance analysis, it is always important to verify 10 | that the monitoring tool is not interfering with the performance numbers. 11 | (eg. writing to disk, using cpu/memory/network, increasing load) 12 | 13 | == Compare with baseline 14 | Depending on the plugins being used and the load on the server itself 15 | the impact Dstat has on the system you are monitoring might be 16 | considerable. A lot of plugins are pretty fast (less than 0.1ms on 17 | an modest 1.2Ghz laptop), but some plugins may use up to 3ms or even 18 | up to 2% of your CPU. (eg. each top-plugin scans the process-list) 19 | 20 | Before performing any tests please verify for yourself what impact 21 | Dstat has on your test results and keep that in mind when analysing 22 | the results afterwards. Especially if you suspect Dstat to be 23 | influencing your results, do a baseline with and without the Dstat 24 | commandline. 25 | 26 | == Selection of plugins 27 | In case the impact is higher than expected, reduce the number of plugins 28 | and remove expensive plugins, or even better, look at the plugin you're 29 | using and send me optimizations. 30 | 31 | Newer python versions are also faster than older ones, and hardware is 32 | only becoming faster at a pace that these considerations may not hold 33 | anylonger. 34 | 35 | == Debugging and profiling Dstat 36 | If you need feedback about plugin performance, use the --debug option 37 | to profile different plugins. If you use -t together with --debug, you 38 | can see the time deviation on your system in relation to load/plugins. 39 | 40 | If you want to profile certain plugins, you can use the --profile option 41 | which provides you with detailed information of the function calls that 42 | are the most expensive. 43 | 44 | You can also run the dstat plugin (--dstat) to look what overhead (cputime) 45 | and response (latency) Dstat has during runtime, which can be very useful 46 | to compare with your baseline and the system in idle state. 47 | 48 | One common way to profile a single plugin is to use the following 49 | commandline: 50 | 51 | dstat -t --dstat --debug --profile 52 | dstat -t --dstat --top-cpu --debug --profile 53 | 54 | The default profiling infrastructure is quite expensive, so it is important 55 | that you first make a baseline including the profiling itself, then 56 | compare it against the same commandline including the plugin you want to 57 | profile. 58 | 59 | == Improving Dstat's footprint even more 60 | Another way to win a few CPU cycles is to pre-compile the Dstat plugins 61 | by running the compileall.py script that comes with python on your 62 | plugins directory. It can save about 10% in execution time. 63 | 64 | Remember that invisible plugins (that run out of your terminal window) 65 | do take up cycles because the information is still being collected and 66 | possibly written to CSV output. 67 | 68 | It should be possible to write plugins in C to improve the impact on 69 | the system, but I have no experience with writing python modules in C. 70 | Any feedback on this is welcomed. 71 | 72 | 73 | == Performance tuning 74 | The following documents may be useful to tune a system for performance 75 | 76 | * http://people.redhat.com/alikins/system_tuning.html[] 77 | 78 | 79 | NOTE: Please send me improvements to this document. 80 | -------------------------------------------------------------------------------- /docs/screen.adoc: -------------------------------------------------------------------------------- 1 | = Configuring screen to display multiple dstat for different systems 2 | 3 | Here is an example of how I monitor 5 nodes in a cluster with a minimum 4 | of effort using screen: 5 | 6 | Put the following content in a file called screenrc-5nodes: 7 | 8 | ---- 9 | startup_message off 10 | defwrap off 11 | split 12 | split 13 | split 14 | split 15 | screen -t node01 1 ssh -t 172.17.0.211 'dstat -cdnyp --tcp --udp -l -D lores,hires -N bond0,eth0,eth2,eth3 10' 16 | focus down 17 | screen -t node02 2 ssh -t 172.17.0.212 'dstat -cdnyp --tcp --udp -l -D lores,hires -N bond0,eth0,eth2,eth3 10' 18 | focus down 19 | screen -t node03 3 ssh -t 172.17.0.213 'dstat -cdnyp --tcp --udp -l -D lores,hires -N bond0,eth0,eth2,eth3 10' 20 | focus down 21 | screen -t node04 4 ssh -t 172.17.0.214 'dstat -cdnyp --tcp --udp -l -D lores,hires -N bond0,eth0,eth2,eth3 10' 22 | focus down 23 | screen -t node05 5 ssh -t 172.17.0.215 'dstat -cdnyp --tcp --udp -l -D lores,hires -N bond0,eth0,eth2,eth3 10' 24 | ---- 25 | 26 | Then set the environment variable to tell screen to use this config-file 27 | for the next screen. 28 | 29 | ---- 30 | SCREENRC='screenrc-5nodes' screen 31 | ---- 32 | 33 | If you want to get out of this screen and end all dstats, the easiest way 34 | is to kill first all regions and then end each dstat. You can do this by: 35 | 36 | ---- 37 | ctrl-a X 38 | ---- 39 | 40 | Do that 5 times, and then quit each dstat by pressing: 41 | 42 | ---- 43 | ctrl-c 44 | ---- 45 | 46 | 5 times. 47 | 48 | If you have other tips or hints, please send them to: 49 | 50 | 51 | NOTE: Please send me improvements to this document. 52 | -------------------------------------------------------------------------------- /dstat.conf: -------------------------------------------------------------------------------- 1 | ### Dstat configuration file 2 | 3 | ### BEWARE: This file is not yet functional, it's a prototype 4 | ### to experiment and find the best syntax for a future dstat 5 | 6 | [main] 7 | interval = 5 8 | diff = 1 9 | colors = true 10 | abs = false 11 | noheader = true 12 | noupdate = true 13 | default-options = -cdns 14 | unit = k 15 | background = light 16 | update-method = interval-average # snapshot total-average last-n-average 17 | 18 | [colors] 19 | default = red yellow green blue magenta cyan white darkred darkgreen 20 | dark = darkred darkyellow darkgreen darkblue darkmagenta darkcyan silver red green 21 | percentage = red yellow green 22 | 23 | [cpu] 24 | show = user sys idle wait 25 | 26 | [ints] 27 | show = 5 9 10 14 15 28 | 29 | [disk] 30 | show = hda hdc lores hires total 31 | 32 | [diskset] 33 | lores = sd[b-t] 34 | hires = sd[u-z] sda[a-d] 35 | total = sd[b-z] sda[a-d] 36 | 37 | [load] 38 | show = 1 5 15 39 | 40 | [mem] 41 | show = used buffers cache free 42 | 43 | [net] 44 | show = bond0 eth0 eth1 45 | #show = bond? eth? 46 | 47 | [proc] 48 | show = run blocked 49 | 50 | [swap] 51 | show = in out 52 | 53 | [sys] 54 | show = int int 55 | 56 | [custom] 57 | load1 = file:///proc/loadavg, line 1, column 1, format %4f 58 | load5 = file:///proc/loadavg, line 1, column 2, format %4f 59 | load15 = file:///proc/loadavg, line 1, column 3, format %4f 60 | int11 = file:///proc/stat, re "^intr ", column 5, format %4d 61 | lo-in = file:///proc/net/dev, re "^lo: ", column 3, format %4d 62 | lo-out = file:///proc/net/dev, re "^lo: ", column 10, format %4d 63 | eth1 = file:///proc/net/dev, re "^eth1: \d+ (\d+) \d+ \d+ \d+ \d+ \d+ \d+ (\d+)", format %4d 64 | switch = snmp://127.0.0.1/net.tcp, format %4d 65 | -------------------------------------------------------------------------------- /examples/curstest: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import curses, sys 3 | 4 | #c = curses.wrapper(s) 5 | #w = curses.initscr() 6 | #curses.start_color() 7 | #print "TERM is", curses.termname() 8 | #if curses.has_colors(): 9 | # print "Has colors"# 10 | #print curses.color_pair(curses.COLOR_RED), "Red" 11 | #curses.endwin() 12 | 13 | #curses.setupterm('xterm') 14 | curses.setupterm() 15 | 16 | if sys.stdout.isatty(): 17 | print "Is a TTY" 18 | 19 | print "Size is %sx%s" % (curses.tigetnum('lines'), curses.tigetnum('cols')) 20 | 21 | if curses.tigetnum('colors') > 0: 22 | print "Has colors" 23 | print curses.tigetnum('colors') 24 | -------------------------------------------------------------------------------- /examples/devtest.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import sys 4 | sys.path.insert(0, '/usr/share/dstat/') 5 | import dstat, time 6 | 7 | devices = ( 8 | ( 1, 0, 'ram0'), 9 | ( 1, 1, 'ram1'), 10 | ( 3, 1, 'hda1'), 11 | ( 33, 0, 'hde'), 12 | ( 7, 0, 'loop0'), 13 | ( 7, 1, 'loop1'), 14 | ( 8, 0, '/dev/sda'), 15 | ( 8, 1, '/dev/sda1'), 16 | ( 8, 18, '/dev/sdb2'), 17 | ( 8, 37, '/dev/sdc5'), 18 | ( 9, 0, 'md0'), 19 | ( 9, 1, 'md1'), 20 | ( 9, 2, 'md2'), 21 | ( 74, 16, '/dev/ida/c2d1'), 22 | ( 77, 241, '/dev/ida/c5d15p1'), 23 | ( 98, 0, 'ubd/disc0/disc'), 24 | ( 98, 16, 'ubd/disc1/disc'), 25 | (104, 0, 'cciss/c0d0'), 26 | (104, 2, 'cciss/c0d0p2'), 27 | (253, 0, 'dm-0'), 28 | (253, 1, 'dm-1'), 29 | ) 30 | 31 | for maj, min, device in devices: 32 | print device, '->', dstat.dev(maj, min) 33 | -------------------------------------------------------------------------------- /examples/dstat.py: -------------------------------------------------------------------------------- 1 | ../dstat -------------------------------------------------------------------------------- /examples/mmpipe.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import select, sys, os 3 | 4 | def readpipe(file, tmout = 0.001): 5 | "Read available data from pipe" 6 | ret = '' 7 | while not select.select([file.fileno()], [], [], tmout)[0]: 8 | pass 9 | while select.select([file.fileno()], [], [], tmout)[0]: 10 | ret = ret + file.read(1) 11 | return ret.split('\n') 12 | 13 | def dpopen(cmd): 14 | "Open a pipe for reuse, if already opened, return pipes" 15 | global pipes 16 | if 'pipes' not in globals().keys(): pipes = {} 17 | if cmd not in pipes.keys(): 18 | try: 19 | import subprocess 20 | p = subprocess.Popen(cmd, shell=False, bufsize=0, close_fds=True, 21 | stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 22 | pipes[cmd] = (p.stdin, p.stdout, p.stderr) 23 | except ImportError: 24 | pipes[cmd] = os.popen3(cmd, 't', 0) 25 | return pipes[cmd] 26 | 27 | ### Unbuffered sys.stdout 28 | sys.stdout = os.fdopen(1, 'w', 0) 29 | 30 | ### Main entrance 31 | if __name__ == '__main__': 32 | try: 33 | # stdin, stdout, stderr = dpopen('/usr/lpp/mmfs/bin/mmpmon -p -s') 34 | # stdin.write('reset\n') 35 | stdin, stdout, stderr = dpopen('/bin/bash') 36 | stdin.write('uname -a\n') 37 | readpipe(stdout) 38 | 39 | while True: 40 | # stdin.write('io_s\n') 41 | stdin.write('cat /proc/stat\n') 42 | for line in readpipe(stdout): 43 | print line 44 | 45 | except KeyboardInterrupt, e: 46 | print 47 | 48 | # vim:ts=4:sw=4 49 | -------------------------------------------------------------------------------- /examples/mstat.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | ### Example2: simple sub-second monitor (ministat) 4 | 5 | ### This is a quick example showing how to implement your own *stat utility 6 | ### If you're interested in such functionality, contact me at dag@wieers.com 7 | import sys 8 | sys.path.insert(0, '/usr/share/dstat/') 9 | import dstat, time 10 | 11 | ### Set default theme 12 | dstat.theme = dstat.set_theme() 13 | 14 | ### Allow arguments 15 | try: delay = float(sys.argv[1]) 16 | except: delay = 0.2 17 | try: count = int(sys.argv[2]) 18 | except: count = 10 19 | 20 | ### Load stats 21 | stats = [] 22 | dstat.starttime = time.time() 23 | dstat.tick = dstat.ticks() 24 | for o in (dstat.dstat_epoch(), dstat.dstat_cpu(), dstat.dstat_mem(), dstat.dstat_load(), dstat.dstat_disk(), dstat.dstat_sys()): 25 | try: o.check() 26 | except Exception, e: print e 27 | else: stats.append(o) 28 | 29 | ### Make time stats sub-second 30 | stats[0].format = ('t', 14, 0) 31 | 32 | ### Print headers 33 | title = subtitle = '' 34 | for o in stats: 35 | title = title + ' ' + o.title() 36 | subtitle = subtitle + ' ' + o.subtitle() 37 | print '\n' + title + '\n' + subtitle 38 | 39 | ### Print stats 40 | for dstat.update in range(count): 41 | line = '' 42 | for o in stats: 43 | o.extract() 44 | line = line + ' ' + o.show() 45 | print line + dstat.ansi['reset'] 46 | if dstat.update != count-1: time.sleep(delay) 47 | dstat.tick = 1 48 | print dstat.ansi['reset'] 49 | -------------------------------------------------------------------------------- /examples/read.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | ### Example 1: Direct accessing stats 4 | ### This is a quick example showing how you can access dstat data 5 | ### If you're interested in this functionality, contact me at dag@wieers.com 6 | import sys 7 | sys.path.insert(0, '/usr/share/dstat/') 8 | import dstat 9 | 10 | ### Set default theme 11 | dstat.theme = dstat.set_theme() 12 | 13 | clear = dstat.ansi['reset'] 14 | dstat.tick = dstat.ticks() 15 | 16 | c = dstat.dstat_cpu() 17 | print c.title() + '\n' + c.subtitle() 18 | c.extract() 19 | print c.show(), clear 20 | print 'Percentage:', c.val['total'] 21 | print 'Raw:', c.cn2['total'] 22 | print 23 | 24 | m = dstat.dstat_mem() 25 | print m.title() + '\n' + m.subtitle() 26 | m.extract() 27 | print m.show(), clear 28 | print 'Raw:', m.val 29 | print 30 | 31 | l = dstat.dstat_load() 32 | print l.title() + '\n' + l.subtitle() 33 | l.extract() 34 | print l.show(), clear 35 | print 'Raw:', l.val 36 | print 37 | 38 | d = dstat.dstat_disk() 39 | print d.title() + '\n' + d.subtitle() 40 | d.extract() 41 | print d.show(), clear 42 | print 'Raw:', d.val['total'] 43 | print 44 | -------------------------------------------------------------------------------- /examples/tdbtest: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import sys, tdb 3 | 4 | db = tdb.tdb('/var/cache/samba/connections.tdb') 5 | print db.keys() 6 | 7 | key=db.firstkey() 8 | while key: 9 | print db.fetch(key) 10 | key=db.nextkey(key) 11 | 12 | db = tdb.tdb('/var/cache/samba/locking.tdb') 13 | print db.keys 14 | 15 | db = tdb.tdb('/var/cache/samba/sessionid.tdb') 16 | print db.keys 17 | 18 | -------------------------------------------------------------------------------- /packaging/snap/python2: -------------------------------------------------------------------------------- 1 | python2.7 -------------------------------------------------------------------------------- /packaging/snap/snapcraft.yaml: -------------------------------------------------------------------------------- 1 | name: dstat 2 | version: 0.7.3 3 | summary: Versatile resource statistic tool 4 | description: dstat is a versatile replacement for vmstat, iostat, and ifstat. 5 | confinement: strict 6 | 7 | apps: 8 | dstat: 9 | command: usr/bin/dstat 10 | plugs: [home, system-observe] 11 | parts: 12 | dstat: 13 | plugin: make 14 | source: https://github.com/dagwieers/dstat/archive/0.7.3.tar.gz 15 | build-packages: [gcc, libc6-dev] 16 | stage-packages: [python2.7] 17 | 18 | python2-symlink: 19 | plugin: copy 20 | files: 21 | python2: usr/bin/ 22 | snap: 23 | - -usr/lib/gcc 24 | - -usr/lib/mime 25 | - -usr/lib/x86_64-linux-gnu 26 | - -lib/x86_64-linux-gnu 27 | - usr/bin/python2 28 | - usr/bin/python2.7 29 | - usr/bin/dstat 30 | -------------------------------------------------------------------------------- /plugins/dstat_battery.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | ### Author: Sven-Hendrik Haase 3 | 4 | class dstat_plugin(dstat): 5 | """ 6 | Percentage of remaining battery power as reported by ACPI. 7 | """ 8 | def __init__(self): 9 | self.name = 'battery' 10 | self.type = 'p' 11 | self.width = 4 12 | self.scale = 34 13 | self.battery_type = "none" 14 | 15 | def check(self): 16 | if os.path.exists('/proc/acpi/battery/'): 17 | self.battery_type = "procfs" 18 | elif glob.glob('/sys/class/power_supply/BAT*'): 19 | self.battery_type = "sysfs" 20 | else: 21 | raise Exception('No ACPI battery information found.') 22 | 23 | def vars(self): 24 | ret = [] 25 | if self.battery_type == "procfs": 26 | for battery in os.listdir('/proc/acpi/battery/'): 27 | for line in dopen('/proc/acpi/battery/'+battery+'/state').readlines(): 28 | l = line.split() 29 | if len(l) < 2: continue 30 | if l[0] == 'present:' and l[1] == 'yes': 31 | ret.append(battery) 32 | elif self.battery_type == "sysfs": 33 | for battery in glob.glob('/sys/class/power_supply/BAT*'): 34 | for line in dopen(battery+'/present').readlines(): 35 | if int(line[0]) == 1: 36 | ret.append(os.path.basename(battery)) 37 | ret.sort() 38 | return ret 39 | 40 | def nick(self): 41 | return [name.lower() for name in self.vars] 42 | 43 | def extract(self): 44 | for battery in self.vars: 45 | if self.battery_type == "procfs": 46 | for line in dopen('/proc/acpi/battery/'+battery+'/info').readlines(): 47 | l = line.split() 48 | if len(l) < 4: continue 49 | if l[0] == 'last': 50 | full = int(l[3]) 51 | break 52 | for line in dopen('/proc/acpi/battery/'+battery+'/state').readlines(): 53 | l = line.split() 54 | if len(l) < 3: continue 55 | if l[0] == 'remaining': 56 | current = int(l[2]) 57 | break 58 | if current: 59 | self.val[battery] = current * 100.0 / full 60 | else: 61 | self.val[battery] = -1 62 | elif self.battery_type == "sysfs": 63 | for line in dopen('/sys/class/power_supply/'+battery+'/capacity').readlines(): 64 | current = int(line) 65 | break 66 | if current: 67 | self.val[battery] = current 68 | else: 69 | self.val[battery] = -1 70 | 71 | # vim:ts=4:sw=4:et 72 | -------------------------------------------------------------------------------- /plugins/dstat_battery_remain.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | """ 5 | Remaining battery time. 6 | 7 | Calculated from power drain and remaining battery power. Information is 8 | retrieved from ACPI. 9 | """ 10 | 11 | def __init__(self): 12 | self.name = 'remain' 13 | self.type = 't' 14 | self.width = 5 15 | self.scale = 0 16 | 17 | def vars(self): 18 | ret = [] 19 | for battery in os.listdir('/proc/acpi/battery/'): 20 | for line in dopen('/proc/acpi/battery/'+battery+'/state').readlines(): 21 | l = line.split() 22 | if len(l) < 2: continue 23 | if l[0] == 'present:' and l[1] == 'yes': 24 | ret.append(battery) 25 | ret.sort() 26 | return ret 27 | 28 | def nick(self): 29 | return [name.lower() for name in self.vars] 30 | 31 | def extract(self): 32 | for battery in self.vars: 33 | for line in dopen('/proc/acpi/battery/'+battery+'/state').readlines(): 34 | l = line.split() 35 | if len(l) < 3: continue 36 | if l[0:2] == ['remaining', 'capacity:']: 37 | remaining = int(l[2]) 38 | continue 39 | elif l[0:2] == ['present', 'rate:']: 40 | rate = int(l[2]) 41 | continue 42 | 43 | if rate and remaining: 44 | self.val[battery] = remaining * 60 / rate 45 | else: 46 | self.val[battery] = -1 47 | 48 | # vim:ts=4:sw=4:et 49 | -------------------------------------------------------------------------------- /plugins/dstat_cpufreq.py: -------------------------------------------------------------------------------- 1 | ### Author: dag@wieers.com 2 | 3 | class dstat_plugin(dstat): 4 | """ 5 | CPU frequency in percentage as reported by ACPI. 6 | """ 7 | 8 | def __init__(self): 9 | self.name = 'frequency' 10 | self.type = 'p' 11 | self.width = 4 12 | self.scale = 34 13 | 14 | def check(self): 15 | for cpu in glob.glob('/sys/devices/system/cpu/cpu[0-9]*'): 16 | if not os.access(cpu+'/cpufreq/scaling_cur_freq', os.R_OK): 17 | raise Exception('Cannot access acpi %s frequency information' % os.path.basename(cpu)) 18 | 19 | def vars(self): 20 | ret = [] 21 | for name in glob.glob('/sys/devices/system/cpu/cpu[0-9]*'): 22 | ret.append(os.path.basename(name)) 23 | ret.sort() 24 | return ret 25 | # return os.listdir('/sys/devices/system/cpu/') 26 | 27 | def nick(self): 28 | return [name.lower() for name in self.vars] 29 | 30 | def extract(self): 31 | for cpu in self.vars: 32 | for line in dopen('/sys/devices/system/cpu/'+cpu+'/cpufreq/scaling_max_freq').readlines(): 33 | l = line.split() 34 | max = int(l[0]) 35 | for line in dopen('/sys/devices/system/cpu/'+cpu+'/cpufreq/scaling_cur_freq').readlines(): 36 | l = line.split() 37 | cur = int(l[0]) 38 | ### Need to close because of bug in sysfs (?) 39 | dclose('/sys/devices/system/cpu/'+cpu+'/cpufreq/scaling_cur_freq') 40 | self.set1[cpu] = self.set1[cpu] + cur * 100.0 / max 41 | 42 | if op.update: 43 | self.val[cpu] = self.set1[cpu] / elapsed 44 | else: 45 | self.val[cpu] = self.set1[cpu] 46 | 47 | if step == op.delay: 48 | self.set1[cpu] = 0 49 | 50 | # vim:ts=4:sw=4:et 51 | -------------------------------------------------------------------------------- /plugins/dstat_dbus.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | """ 5 | Number of active dbus sessions. 6 | """ 7 | def __init__(self): 8 | self.name = 'dbus' 9 | self.nick = ('sys', 'ses') 10 | self.vars = ('system', 'session') 11 | self.type = 'd' 12 | self.width = 3 13 | self.scale = 100 14 | 15 | def check(self): 16 | # dstat.info(1, 'The dbus module is an EXPERIMENTAL module.') 17 | try: 18 | global dbus 19 | import dbus 20 | try: 21 | self.sysbus = dbus.Bus(dbus.Bus.TYPE_SYSTEM).get_service('org.freedesktop.DBus').get_object('/org/freedesktop/DBus', 'org.freedesktop.DBus') 22 | try: 23 | self.sesbus = dbus.Bus(dbus.Bus.TYPE_SESSION).get_service('org.freedesktop.DBus').get_object('/org/freedesktop/DBus', 'org.freedesktop.DBus') 24 | except: 25 | self.sesbus = None 26 | except: 27 | raise Exception('Unable to connect to dbus message bus') 28 | except: 29 | raise Exception('Needs python-dbus module') 30 | 31 | def extract(self): 32 | self.val['system'] = len(self.sysbus.ListServices()) - 1 33 | try: 34 | self.val['session'] = len(self.sesbus.ListServices()) - 1 35 | except: 36 | self.val['session'] = -1 37 | # print(dir(b)); print(dir(s)); print(dir(d)); print(d.ListServices()) 38 | # print(dir(d)) 39 | # print(d.ListServices()) 40 | 41 | # vim:ts=4:sw=4:et 42 | -------------------------------------------------------------------------------- /plugins/dstat_disk_avgqu.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | """ 5 | The average queue length of the requests that were issued to the device. 6 | """ 7 | 8 | def __init__(self): 9 | self.version = 2 10 | self.nick = ('avgqu',) 11 | self.type = 'f' 12 | self.width = 4 13 | self.scale = 10 14 | self.diskfilter = re.compile('^([hsv]d[a-z]+\d+|cciss/c\d+d\d+p\d+|dm-\d+|md\d+|mmcblk\d+p\d0|VxVM\d+)$') 15 | self.open('/proc/diskstats') 16 | self.cols = 1 17 | self.struct = dict( rq_ticks=0 ) 18 | 19 | def discover(self, *objlist): 20 | ret = [] 21 | for l in self.splitlines(): 22 | if len(l) < 13: continue 23 | if l[3:] == ['0',] * 11: continue 24 | name = l[2] 25 | ret.append(name) 26 | for item in objlist: ret.append(item) 27 | if not ret: 28 | raise Exception('No suitable block devices found to monitor') 29 | return ret 30 | 31 | def vars(self): 32 | ret = [] 33 | if op.disklist: 34 | varlist = op.disklist 35 | else: 36 | varlist = [] 37 | blockdevices = [os.path.basename(filename) for filename in glob.glob('/sys/block/*')] 38 | for name in self.discover: 39 | if self.diskfilter.match(name): continue 40 | if name not in blockdevices: continue 41 | varlist.append(name) 42 | varlist.sort() 43 | for name in varlist: 44 | if name in self.discover: 45 | ret.append(name) 46 | return ret 47 | 48 | def name(self): 49 | return self.vars 50 | 51 | def extract(self): 52 | for l in self.splitlines(): 53 | if len(l) < 13: continue 54 | if l[3:] == ['0',] * 11: continue 55 | if l[3] == '0' and l[7] == '0': continue 56 | name = l[2] 57 | if name not in self.vars or name == 'total': continue 58 | self.set2[name] = dict( 59 | rq_ticks = int(l[13]), 60 | ) 61 | 62 | for name in self.vars: 63 | self.val[name] = ( ( self.set2[name]['rq_ticks'] - self.set1[name]['rq_ticks'] ) * 1.0 / elapsed / 1000, ) 64 | 65 | if step == op.delay: 66 | self.set1.update(self.set2) 67 | -------------------------------------------------------------------------------- /plugins/dstat_disk_avgrq.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | """ 5 | The average size (in sectors) of the requests that were issued 6 | to the device. 7 | """ 8 | 9 | def __init__(self): 10 | self.version = 2 11 | self.nick = ('avgrq',) 12 | self.type = 'f' 13 | self.width = 4 14 | self.scale = 10 15 | self.diskfilter = re.compile('^([hsv]d[a-z]+\d+|cciss/c\d+d\d+p\d+|dm-\d+|md\d+|mmcblk\d+p\d0|VxVM\d+)$') 16 | self.open('/proc/diskstats') 17 | self.cols = 1 18 | self.struct = dict( nr_ios=0, rd_sect=0, wr_sect=0 ) 19 | 20 | def discover(self, *objlist): 21 | ret = [] 22 | for l in self.splitlines(): 23 | if len(l) < 13: continue 24 | if l[3:] == ['0',] * 11: continue 25 | name = l[2] 26 | ret.append(name) 27 | for item in objlist: ret.append(item) 28 | if not ret: 29 | raise Exception('No suitable block devices found to monitor') 30 | return ret 31 | 32 | def vars(self): 33 | ret = [] 34 | if op.disklist: 35 | varlist = op.disklist 36 | else: 37 | varlist = [] 38 | blockdevices = [os.path.basename(filename) for filename in glob.glob('/sys/block/*')] 39 | for name in self.discover: 40 | if self.diskfilter.match(name): continue 41 | if name not in blockdevices: continue 42 | varlist.append(name) 43 | varlist.sort() 44 | for name in varlist: 45 | if name in self.discover: 46 | ret.append(name) 47 | return ret 48 | 49 | def name(self): 50 | return self.vars 51 | 52 | def extract(self): 53 | for l in self.splitlines(): 54 | if len(l) < 13: continue 55 | if l[3:] == ['0',] * 11: continue 56 | if l[3] == '0' and l[7] == '0': continue 57 | name = l[2] 58 | if name not in self.vars or name == 'total': continue 59 | self.set2[name] = dict( 60 | nr_ios = int(l[3])+int(l[7]), 61 | rd_sect = int(l[9]), 62 | wr_sect = int(l[11]), 63 | ) 64 | 65 | for name in self.vars: 66 | tput = ( self.set2[name]['nr_ios'] - self.set1[name]['nr_ios'] ) 67 | if tput: 68 | ticks = self.set2[name]['rd_sect'] - self.set1[name]['rd_sect'] + \ 69 | self.set2[name]['wr_sect'] - self.set1[name]['wr_sect'] 70 | self.val[name] = ( ticks * 1.0 / tput, ) 71 | else: 72 | self.val[name] = ( 0.0, ) 73 | 74 | if step == op.delay: 75 | self.set1.update(self.set2) 76 | -------------------------------------------------------------------------------- /plugins/dstat_disk_svctm.py: -------------------------------------------------------------------------------- 1 | ### Author: David Nicklay 2 | ### Modified from disk-util: Dag Wieers 3 | 4 | class dstat_plugin(dstat): 5 | """ 6 | The average service time (in milliseconds) for I/O requests that were 7 | issued to the device. 8 | 9 | Warning! Do not trust this field any more. 10 | """ 11 | 12 | def __init__(self): 13 | self.version = 2 14 | self.nick = ('svctm',) 15 | self.type = 'f' 16 | self.width = 4 17 | self.scale = 1 18 | self.diskfilter = re.compile('^([hsv]d[a-z]+\d+|cciss/c\d+d\d+p\d+|dm-\d+|md\d+|mmcblk\d+p\d0|VxVM\d+)$') 19 | self.open('/proc/diskstats') 20 | self.cols = 1 21 | self.struct = dict( nr_ios=0, tot_ticks=0 ) 22 | 23 | def discover(self, *objlist): 24 | ret = [] 25 | for l in self.splitlines(): 26 | if len(l) < 13: continue 27 | if l[3:] == ['0',] * 11: continue 28 | name = l[2] 29 | ret.append(name) 30 | for item in objlist: ret.append(item) 31 | if not ret: 32 | raise Exception('No suitable block devices found to monitor') 33 | return ret 34 | 35 | def vars(self): 36 | ret = [] 37 | if op.disklist: 38 | varlist = op.disklist 39 | else: 40 | varlist = [] 41 | blockdevices = [os.path.basename(filename) for filename in glob.glob('/sys/block/*')] 42 | for name in self.discover: 43 | if self.diskfilter.match(name): continue 44 | if name not in blockdevices: continue 45 | varlist.append(name) 46 | varlist.sort() 47 | for name in varlist: 48 | if name in self.discover: 49 | ret.append(name) 50 | return ret 51 | 52 | def name(self): 53 | return self.vars 54 | 55 | def extract(self): 56 | for l in self.splitlines(): 57 | if len(l) < 13: continue 58 | if l[3:] == ['0',] * 11: continue 59 | if l[3] == '0' and l[7] == '0': continue 60 | name = l[2] 61 | if name not in self.vars or name == 'total': continue 62 | self.set2[name] = dict( 63 | nr_ios = int(l[3])+int(l[7]), 64 | tot_ticks = int(l[12]), 65 | ) 66 | 67 | for name in self.vars: 68 | tput = ( self.set2[name]['nr_ios'] - self.set1[name]['nr_ios'] ) 69 | if tput: 70 | util = ( self.set2[name]['tot_ticks'] - self.set1[name]['tot_ticks'] ) 71 | self.val[name] = ( util * 1.0 / tput, ) 72 | else: 73 | self.val[name] = ( 0.0, ) 74 | 75 | if step == op.delay: 76 | self.set1.update(self.set2) 77 | -------------------------------------------------------------------------------- /plugins/dstat_disk_tps.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | """ 5 | Number of read and write transactions per device. 6 | 7 | Displays the number of read and write I/O transactions per device. 8 | """ 9 | 10 | def __init__(self): 11 | self.nick = ('#read', '#writ' ) 12 | self.type = 'd' 13 | self.width = 5 14 | self.scale = 1000 15 | self.diskfilter = re.compile('^([hsv]d[a-z]+\d+|cciss/c\d+d\d+p\d+|dm-\d+|md\d+|mmcblk\d+p\d0|VxVM\d+)$') 16 | self.open('/proc/diskstats') 17 | self.cols = 2 18 | 19 | def discover(self, *objlist): 20 | ret = [] 21 | for l in self.splitlines(): 22 | if len(l) < 13: continue 23 | if l[3:] == ['0',] * 11: continue 24 | name = l[2] 25 | ret.append(name) 26 | for item in objlist: ret.append(item) 27 | if not ret: 28 | raise Exception('No suitable block devices found to monitor') 29 | return ret 30 | 31 | def vars(self): 32 | ret = [] 33 | if op.disklist: 34 | varlist = op.disklist 35 | elif not op.full: 36 | varlist = ('total',) 37 | else: 38 | varlist = [] 39 | for name in self.discover: 40 | if self.diskfilter.match(name): continue 41 | if name not in blockdevices(): continue 42 | varlist.append(name) 43 | # if len(varlist) > 2: varlist = varlist[0:2] 44 | varlist.sort() 45 | for name in varlist: 46 | if name in self.discover + ['total'] or name in op.diskset: 47 | ret.append(name) 48 | return ret 49 | 50 | def name(self): 51 | return ['dsk/'+sysfs_dev(name) for name in self.vars] 52 | 53 | def extract(self): 54 | for name in self.vars: self.set2[name] = (0, 0) 55 | for l in self.splitlines(): 56 | if len(l) < 13: continue 57 | if l[3] == '0' and l[7] == '0': continue 58 | if l[3:] == ['0',] * 11: continue 59 | name = l[2] 60 | if not self.diskfilter.match(name): 61 | self.set2['total'] = ( self.set2['total'][0] + int(l[3]), self.set2['total'][1] + int(l[7]) ) 62 | if name in self.vars and name != 'total': 63 | self.set2[name] = ( self.set2[name][0] + int(l[3]), self.set2[name][1] + int(l[7])) 64 | for diskset in self.vars: 65 | if diskset in op.diskset: 66 | for disk in op.diskset[diskset]: 67 | if re.match('^'+disk+'$', name): 68 | self.set2[diskset] = ( self.set2[diskset][0] + int(l[3]), self.set2[diskset][1] + int(l[7]) ) 69 | 70 | for name in self.set2: 71 | self.val[name] = list(map(lambda x, y: (y - x) / elapsed, self.set1[name], self.set2[name])) 72 | 73 | if step == op.delay: 74 | self.set1.update(self.set2) 75 | -------------------------------------------------------------------------------- /plugins/dstat_disk_util.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | """ 5 | Percentage of bandwidth utilization for block devices. 6 | 7 | Displays percentage of CPU time during which I/O requests were issued 8 | to the device (bandwidth utilization for the device). Device saturation 9 | occurs when this value is close to 100%. 10 | """ 11 | 12 | def __init__(self): 13 | self.nick = ('util', ) 14 | self.type = 'f' 15 | self.width = 4 16 | self.scale = 34 17 | self.diskfilter = re.compile('^([hsv]d[a-z]+\d+|cciss/c\d+d\d+p\d+|dm-\d+|md\d+|mmcblk\d+p\d0|VxVM\d+)$') 18 | self.open('/proc/diskstats') 19 | self.cols = 1 20 | self.struct = dict( tot_ticks=0 ) 21 | 22 | def discover(self, *objlist): 23 | ret = [] 24 | for l in self.splitlines(): 25 | if len(l) < 13: continue 26 | if l[3:] == ['0',] * 11: continue 27 | name = l[2] 28 | ret.append(name) 29 | for item in objlist: ret.append(item) 30 | if not ret: 31 | raise Exception('No suitable block devices found to monitor') 32 | return ret 33 | 34 | def basename(self, disk): 35 | "Strip /dev/ and convert symbolic link" 36 | if disk[:5] == '/dev/': 37 | # file or symlink 38 | if os.path.exists(disk): 39 | # e.g. /dev/disk/by-uuid/15e40cc5-85de-40ea-b8fb-cb3a2eaf872 40 | if os.path.islink(disk): 41 | target = os.readlink(disk) 42 | # convert relative pathname to absolute 43 | if target[0] != '/': 44 | target = os.path.join(os.path.dirname(disk), target) 45 | target = os.path.normpath(target) 46 | print('dstat: symlink %s -> %s' % (disk, target)) 47 | disk = target 48 | # trim leading /dev/ 49 | return disk[5:] 50 | else: 51 | print('dstat: %s does not exist' % disk) 52 | else: 53 | return disk 54 | 55 | def vars(self): 56 | ret = [] 57 | if op.disklist: 58 | varlist = list(map(self.basename, op.disklist)) 59 | else: 60 | varlist = [] 61 | for name in self.discover: 62 | if self.diskfilter.match(name): continue 63 | if name not in blockdevices(): continue 64 | varlist.append(name) 65 | # if len(varlist) > 2: varlist = varlist[0:2] 66 | varlist.sort() 67 | for name in varlist: 68 | if name in self.discover: 69 | ret.append(name) 70 | return ret 71 | 72 | def name(self): 73 | return [sysfs_dev(name) for name in self.vars] 74 | 75 | def extract(self): 76 | for l in self.splitlines(): 77 | if len(l) < 13: continue 78 | if l[5] == '0' and l[9] == '0': continue 79 | if l[3:] == ['0',] * 11: continue 80 | name = l[2] 81 | if name not in self.vars: continue 82 | self.set2[name] = dict( 83 | tot_ticks = int(l[12]) 84 | ) 85 | 86 | for name in self.vars: 87 | self.val[name] = ( (self.set2[name]['tot_ticks'] - self.set1[name]['tot_ticks']) * 1.0 * hz / elapsed / 1000, ) 88 | 89 | if step == op.delay: 90 | self.set1.update(self.set2) 91 | -------------------------------------------------------------------------------- /plugins/dstat_disk_wait.py: -------------------------------------------------------------------------------- 1 | ### Author: David Nicklay 2 | ### Modified from disk-util: Dag Wieers 3 | 4 | class dstat_plugin(dstat): 5 | """ 6 | Read and Write average wait times of block devices. 7 | 8 | Displays the average read and write wait times of block devices 9 | """ 10 | 11 | def __init__(self): 12 | self.nick = ('rawait', 'wawait') 13 | self.type = 'f' 14 | self.width = 4 15 | self.scale = 1 16 | self.diskfilter = re.compile('^([hsv]d[a-z]+\d+|cciss/c\d+d\d+p\d+|dm-\d+|md\d+|mmcblk\d+p\d0|VxVM\d+)$') 17 | self.open('/proc/diskstats') 18 | self.cols = 1 19 | self.struct = dict( rd_ios=0, wr_ios=0, rd_ticks=0, wr_ticks=0 ) 20 | 21 | def discover(self, *objlist): 22 | ret = [] 23 | for l in self.splitlines(): 24 | if len(l) < 13: continue 25 | if l[3:] == ['0',] * 11: continue 26 | name = l[2] 27 | ret.append(name) 28 | for item in objlist: ret.append(item) 29 | if not ret: 30 | raise Exception('No suitable block devices found to monitor') 31 | return ret 32 | 33 | def vars(self): 34 | ret = [] 35 | if op.disklist: 36 | varlist = op.disklist 37 | else: 38 | varlist = [] 39 | blockdevices = [os.path.basename(filename) for filename in glob.glob('/sys/block/*')] 40 | for name in self.discover: 41 | if self.diskfilter.match(name): continue 42 | if name not in blockdevices: continue 43 | varlist.append(name) 44 | varlist.sort() 45 | for name in varlist: 46 | if name in self.discover: 47 | ret.append(name) 48 | return ret 49 | 50 | def name(self): 51 | return self.vars 52 | 53 | def extract(self): 54 | for l in self.splitlines(): 55 | if len(l) < 13: continue 56 | if l[5] == '0' and l[9] == '0': continue 57 | if l[3:] == ['0',] * 11: continue 58 | name = l[2] 59 | if name not in self.vars: continue 60 | self.set2[name] = dict( 61 | rd_ios = int(l[3]), 62 | wr_ios = int(l[7]), 63 | rd_ticks = int(l[6]), 64 | wr_ticks = int(l[10]), 65 | ) 66 | 67 | for name in self.vars: 68 | rd_tput = self.set2[name]['rd_ios'] - self.set1[name]['rd_ios'] 69 | wr_tput = self.set2[name]['wr_ios'] - self.set1[name]['wr_ios'] 70 | if rd_tput: 71 | rd_wait = ( self.set2[name]['rd_ticks'] - self.set1[name]['rd_ticks'] ) * 1.0 / rd_tput 72 | else: 73 | rd_wait = 0 74 | if wr_tput: 75 | wr_wait = ( self.set2[name]['wr_ticks'] - self.set1[name]['wr_ticks'] ) * 1.0 / wr_tput 76 | else: 77 | wr_wait = 0 78 | self.val[name] = ( rd_wait, wr_wait ) 79 | 80 | if step == op.delay: 81 | self.set1.update(self.set2) 82 | -------------------------------------------------------------------------------- /plugins/dstat_dstat.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | """ 5 | Provide more information related to the dstat process. 6 | 7 | The dstat cputime is the total cputime dstat requires per second. On a 8 | system with one cpu and one core, the total cputime is 1000ms. On a system 9 | with 2 cores the total is 2000ms. It may help to vizualise the performance 10 | of Dstat and its selection of plugins. 11 | """ 12 | def __init__(self): 13 | self.name = 'dstat' 14 | self.vars = ('cputime', 'latency') 15 | self.type = 'd' 16 | self.width = 5 17 | self.scale = 1000 18 | self.open('/proc/%s/schedstat' % ownpid) 19 | 20 | def extract(self): 21 | l = self.splitline() 22 | # l = linecache.getline('/proc/%s/schedstat' % self.pid, 1).split() 23 | self.set2['cputime'] = int(l[0]) 24 | self.set2['latency'] = int(l[1]) 25 | 26 | for name in self.vars: 27 | self.val[name] = (self.set2[name] - self.set1[name]) * 1.0 / elapsed 28 | 29 | if step == op.delay: 30 | self.set1.update(self.set2) 31 | 32 | # vim:ts=4:sw=4:et 33 | -------------------------------------------------------------------------------- /plugins/dstat_dstat_cpu.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | """ 5 | Provide CPU information related to the dstat process. 6 | 7 | This plugin shows the CPU utilization for the dstat process itself, 8 | including the user-space and system-space (kernel) utilization and 9 | a total of both. On a system with one cpu and one core, the total 10 | cputime is 1000ms. On a system with 2 cores the total is 2000ms. 11 | It may help to vizualise the performance of Dstat and its selection 12 | of plugins. 13 | """ 14 | def __init__(self): 15 | self.name = 'dstat cpu' 16 | self.vars = ('user', 'system', 'total') 17 | self.nick = ('usr', 'sys', 'tot') 18 | self.type = 'p' 19 | self.width = 3 20 | self.scale = 100 21 | 22 | def extract(self): 23 | res = resource.getrusage(resource.RUSAGE_SELF) 24 | 25 | self.set2['user'] = float(res.ru_utime) 26 | self.set2['system'] = float(res.ru_stime) 27 | self.set2['total'] = float(res.ru_utime) + float(res.ru_stime) 28 | 29 | for name in self.vars: 30 | self.val[name] = (self.set2[name] - self.set1[name]) * 100.0 / elapsed / cpunr 31 | 32 | if step == op.delay: 33 | self.set1.update(self.set2) 34 | 35 | # vim:ts=4:sw=4:et 36 | -------------------------------------------------------------------------------- /plugins/dstat_dstat_ctxt.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | """ 5 | Provide Dstat's number of voluntary and involuntary context switches. 6 | 7 | This plugin provides a unique view of the number of voluntary and 8 | involuntary context switches of the Dstat process itself. It may help 9 | to vizualise the performance of Dstat and its selection of plugins. 10 | """ 11 | def __init__(self): 12 | self.name = 'contxt sw' 13 | self.vars = ('voluntary', 'involuntary', 'total') 14 | self.type = 'd' 15 | self.width = 3 16 | self.scale = 100 17 | 18 | def extract(self): 19 | res = resource.getrusage(resource.RUSAGE_SELF) 20 | 21 | self.set2['voluntary'] = float(res.ru_nvcsw) 22 | self.set2['involuntary'] = float(res.ru_nivcsw) 23 | self.set2['total'] = (float(res.ru_nvcsw) + float(res.ru_nivcsw)) 24 | 25 | for name in self.vars: 26 | self.val[name] = (self.set2[name] - self.set1[name]) * 1.0 / elapsed 27 | 28 | if step == op.delay: 29 | self.set1.update(self.set2) 30 | 31 | # vim:ts=4:sw=4:et 32 | -------------------------------------------------------------------------------- /plugins/dstat_dstat_mem.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | """ 5 | Provide memory information related to the dstat process. 6 | 7 | The various values provide information about the memory usage of the 8 | dstat process. This plugin gives you the possibility to follow memory 9 | usage changes of dstat over time. It may help to vizualise the 10 | performance of Dstat and its selection of plugins. 11 | """ 12 | def __init__(self): 13 | self.name = 'dstat memory usage' 14 | self.vars = ('virtual', 'resident', 'shared', 'data') 15 | self.type = 'd' 16 | self.open('/proc/%s/statm' % ownpid) 17 | 18 | def extract(self): 19 | l = self.splitline() 20 | # l = linecache.getline('/proc/%s/schedstat' % self.pid, 1).split() 21 | self.val['virtual'] = int(l[0]) * pagesize / 1024 22 | self.val['resident'] = int(l[1]) * pagesize / 1024 23 | self.val['shared'] = int(l[2]) * pagesize / 1024 24 | # self.val['text'] = int(l[3]) * pagesize / 1024 25 | # self.val['library'] = int(l[4]) * pagesize / 1024 26 | self.val['data'] = int(l[5]) * pagesize / 1024 27 | 28 | # vim:ts=4:sw=4:et 29 | -------------------------------------------------------------------------------- /plugins/dstat_fan.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | """ 5 | Fan speed in RPM (rotations per minute) as reported by ACPI. 6 | """ 7 | 8 | def __init__(self): 9 | self.name = 'fan' 10 | self.type = 'd' 11 | self.width = 4 12 | self.scale = 500 13 | self.open('/proc/acpi/ibm/fan') 14 | 15 | def vars(self): 16 | ret = None 17 | for l in self.splitlines(): 18 | if l[0] == 'speed:': 19 | ret = ('speed',) 20 | return ret 21 | 22 | def check(self): 23 | if not os.path.exists('/proc/acpi/ibm/fan'): 24 | raise Exception('Needs kernel IBM-ACPI support') 25 | 26 | def extract(self): 27 | if os.path.exists('/proc/acpi/ibm/fan'): 28 | for l in self.splitlines(): 29 | if l[0] == 'speed:': 30 | self.val['speed'] = int(l[1]) 31 | 32 | # vim:ts=4:sw=4:et 33 | -------------------------------------------------------------------------------- /plugins/dstat_freespace.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | ### FIXME: This module needs infrastructure to provide a list of mountpoints 4 | ### FIXME: Would be nice to have a total by default (half implemented) 5 | 6 | class dstat_plugin(dstat): 7 | """ 8 | Amount of used and free space per mountpoint. 9 | """ 10 | 11 | def __init__(self): 12 | self.nick = ('used', 'free') 13 | self.open('/etc/mtab') 14 | self.cols = 2 15 | 16 | def vars(self): 17 | ret = [] 18 | for l in self.splitlines(): 19 | if len(l) < 6: continue 20 | if l[2] in ('binfmt_misc', 'devpts', 'iso9660', 'none', 'proc', 'sysfs', 'usbfs', 'cgroup', 'tmpfs', 'devtmpfs', 'debugfs', 'mqueue', 'systemd-1', 'rootfs', 'autofs'): continue 21 | ### FIXME: Excluding 'none' here may not be what people want (/dev/shm) 22 | if l[0] in ('devpts', 'none', 'proc', 'sunrpc', 'usbfs', 'securityfs', 'hugetlbfs', 'configfs', 'selinuxfs', 'pstore', 'nfsd'): continue 23 | name = l[1] 24 | res = os.statvfs(name) 25 | if res[0] == 0: continue ### Skip zero block filesystems 26 | ret.append(name) 27 | 28 | #print(l[0] + " / " + name + " / " + l[2]) 29 | return ret 30 | 31 | def name(self): 32 | return ['/' + os.path.basename(name) for name in self.vars] 33 | 34 | def extract(self): 35 | self.val['total'] = (0, 0) 36 | for name in self.vars: 37 | res = os.statvfs(name) 38 | self.val[name] = ( (float(res.f_blocks) - float(res.f_bavail)) * int(res.f_frsize), float(res.f_bavail) * float(res.f_frsize) ) 39 | self.val['total'] = (self.val['total'][0] + self.val[name][0], self.val['total'][1] + self.val[name][1]) 40 | 41 | # vim:ts=4:sw=4:et 42 | -------------------------------------------------------------------------------- /plugins/dstat_fuse.py: -------------------------------------------------------------------------------- 1 | ### Author: Vikas Gorur (http://github.com/vikasgorur) 2 | 3 | class dstat_plugin(dstat): 4 | """ 5 | Waiting calls on mounted FUSE filesystems 6 | 7 | Displays the number of waiting calls on all mounted FUSE filesystems. 8 | """ 9 | 10 | def __init__(self): 11 | self.name = 'fuse' 12 | self.type = 'd' 13 | self.fusectl_path = "/sys/fs/fuse/connections/" 14 | self.dirs = [] 15 | 16 | def check(self): 17 | info(1, "Module %s is still experimental." % self.filename) 18 | 19 | if not os.path.exists(self.fusectl_path): 20 | raise Exception('%s not mounted' % self.fusectl_path) 21 | if len(os.listdir(self.fusectl_path)) == 0: 22 | raise Exception('No fuse filesystems mounted') 23 | 24 | def vars(self): 25 | self.dirs = os.listdir(self.fusectl_path) 26 | 27 | atleast_one_ok = False 28 | for d in self.dirs: 29 | if os.access(self.fusectl_path + d + "/waiting", os.R_OK): 30 | atleast_one_ok = True 31 | 32 | if not atleast_one_ok: 33 | raise Exception('User is not root or no fuse filesystems mounted') 34 | 35 | return self.dirs 36 | 37 | def extract(self): 38 | for d in self.dirs: 39 | path = self.fusectl_path + d + "/waiting" 40 | if os.path.exists(path): 41 | line = dopen(path).readline() 42 | self.val[d] = int(line) 43 | else: 44 | self.val[d] = 0 45 | 46 | # vim:ts=4:sw=4:et 47 | -------------------------------------------------------------------------------- /plugins/dstat_gpfs.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | """ 5 | Total amount of read and write throughput (in bytes) on a GPFS filesystem. 6 | """ 7 | 8 | def __init__(self): 9 | self.name = 'gpfs i/o' 10 | self.nick = ('read', 'write') 11 | self.vars = ('_br_', '_bw_') 12 | 13 | def check(self): 14 | if os.access('/usr/lpp/mmfs/bin/mmpmon', os.X_OK): 15 | try: 16 | self.stdin, self.stdout, self.stderr = dpopen('/usr/lpp/mmfs/bin/mmpmon -p -s') 17 | self.stdin.write('reset\n') 18 | readpipe(self.stdout) 19 | except IOError: 20 | raise Exception('Cannot interface with gpfs mmpmon binary') 21 | return True 22 | raise Exception('Needs GPFS mmpmon binary') 23 | 24 | def extract(self): 25 | try: 26 | self.stdin.write('io_s\n') 27 | # readpipe(self.stderr) 28 | for line in readpipe(self.stdout): 29 | if not line: continue 30 | l = line.split() 31 | for name in self.vars: 32 | self.set2[name] = int(l[l.index(name)+1]) 33 | for name in self.vars: 34 | self.val[name] = (self.set2[name] - self.set1[name]) * 1.0 / elapsed 35 | except IOError as e: 36 | if op.debug > 1: print('%s: lost pipe to mmpmon, %s' % (self.filename, e)) 37 | for name in self.vars: self.val[name] = -1 38 | except Exception as e: 39 | if op.debug > 1: print('%s: exception %s' % (self.filename, e)) 40 | for name in self.vars: self.val[name] = -1 41 | 42 | if step == op.delay: 43 | self.set1.update(self.set2) 44 | 45 | # vim:ts=4:sw=4:et 46 | -------------------------------------------------------------------------------- /plugins/dstat_gpfs_ops.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | """ 5 | Number of operations performed on a GPFS filesystem. 6 | """ 7 | 8 | def __init__(self): 9 | self.name = 'gpfs file operations' 10 | self.nick = ('open', 'clos', 'read', 'writ', 'rdir', 'inod') 11 | self.vars = ('_oc_', '_cc_', '_rdc_', '_wc_', '_dir_', '_iu_') 12 | self.type = 'd' 13 | self.width = 5 14 | self.scale = 1000 15 | 16 | def check(self): 17 | if os.access('/usr/lpp/mmfs/bin/mmpmon', os.X_OK): 18 | try: 19 | self.stdin, self.stdout, self.stderr = dpopen('/usr/lpp/mmfs/bin/mmpmon -p -s') 20 | self.stdin.write('reset\n') 21 | readpipe(self.stdout) 22 | except IOError: 23 | raise Exception('Cannot interface with gpfs mmpmon binary') 24 | return True 25 | raise Exception('Needs GPFS mmpmon binary') 26 | 27 | def extract(self): 28 | try: 29 | self.stdin.write('io_s\n') 30 | # readpipe(self.stderr) 31 | for line in readpipe(self.stdout): 32 | if not line: continue 33 | l = line.split() 34 | for name in self.vars: 35 | self.set2[name] = int(l[l.index(name)+1]) 36 | for name in self.vars: 37 | self.val[name] = (self.set2[name] - self.set1[name]) * 1.0 / elapsed 38 | except IOError as e: 39 | if op.debug > 1: print('%s: lost pipe to mmpmon, %s' % (self.filename, e)) 40 | for name in self.vars: self.val[name] = -1 41 | except Exception as e: 42 | if op.debug > 1: print('%s: exception %s' % (self.filename, e)) 43 | for name in self.vars: self.val[name] = -1 44 | 45 | if step == op.delay: 46 | self.set1.update(self.set2) 47 | 48 | # vim:ts=4:sw=4:et 49 | -------------------------------------------------------------------------------- /plugins/dstat_helloworld.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | """ 5 | Example "Hello world!" output plugin for aspiring Dstat developers. 6 | """ 7 | 8 | def __init__(self): 9 | self.name = 'plugin title' 10 | self.nick = ('counter',) 11 | self.vars = ('text',) 12 | self.type = 's' 13 | self.width = 12 14 | self.scale = 0 15 | 16 | def extract(self): 17 | self.val['text'] = 'Hello world!' 18 | 19 | # vim:ts=4:sw=4:et 20 | -------------------------------------------------------------------------------- /plugins/dstat_ib.py: -------------------------------------------------------------------------------- 1 | ### Author: Dmitry Fedin 2 | 3 | 4 | class dstat_plugin(dstat): 5 | ibdirname = '/sys/class/infiniband' 6 | """ 7 | Bytes received or sent through infiniband/RoCE interfaces 8 | Usage: 9 | dstat --ib -N :,total 10 | default dstat --ib is the same as 11 | dstat --ib -N total 12 | 13 | example for Mellanox adapter, transfering data via port 2 14 | dstat --ib -Nmlx4_0:2 15 | """ 16 | 17 | def __init__(self): 18 | self.nick = ('recv', 'send') 19 | self.type = 'd' 20 | self.cols = 2 21 | self.width = 6 22 | 23 | def discover(self, *objlist): 24 | ret = [] 25 | for subdirname in os.listdir(self.ibdirname): 26 | if not os.path.isdir(os.path.join(self.ibdirname,subdirname)) : continue 27 | device_dir = os.path.join(self.ibdirname, subdirname, 'ports') 28 | for subdirname2 in os.listdir(device_dir) : 29 | if not os.path.isdir(os.path.join(device_dir,subdirname2)): continue 30 | name = subdirname + ":" + subdirname2 31 | ret.append(name) 32 | ret.sort() 33 | for item in objlist: ret.append(item) 34 | return ret 35 | 36 | def vars(self): 37 | ret = [] 38 | if op.netlist: 39 | varlist = op.netlist 40 | elif not op.full: 41 | varlist = ('total',) 42 | else: 43 | varlist = self.discover 44 | varlist.sort() 45 | for name in varlist: 46 | if name in self.discover + ['total']: 47 | ret.append(name) 48 | if not ret: 49 | raise Exception('No suitable network interfaces found to monitor') 50 | return ret 51 | 52 | def name(self): 53 | return ['ib/'+name for name in self.vars] 54 | 55 | def extract(self): 56 | self.set2['total'] = [0, 0] 57 | ifaces = self.discover 58 | for name in self.vars: self.set2[name] = [0, 0] 59 | for name in ifaces: 60 | l=name.split(':'); 61 | if len(l) < 2: 62 | continue 63 | rcv_counter_name=os.path.join('/sys/class/infiniband', l[0], 'ports', l[1], 'counters_ext/port_rcv_data_64') 64 | xmit_counter_name=os.path.join('/sys/class/infiniband', l[0], 'ports', l[1], 'counters_ext/port_xmit_data_64') 65 | rcv_lines = dopen(rcv_counter_name).readlines() 66 | xmit_lines = dopen(xmit_counter_name).readlines() 67 | if len(rcv_lines) < 1 or len(xmit_lines) < 1: 68 | continue 69 | rcv_value = int(rcv_lines[0]) 70 | xmit_value = int(xmit_lines[0]) 71 | if name in self.vars : 72 | self.set2[name] = (rcv_value, xmit_value) 73 | self.set2['total'] = ( self.set2['total'][0] + rcv_value, self.set2['total'][1] + xmit_value) 74 | if update: 75 | for name in self.set2: 76 | self.val[name] = [ 77 | (self.set2[name][0] - self.set1[name][0]) * 4.0 / elapsed, 78 | (self.set2[name][1] - self.set1[name][1]) * 4.0/ elapsed, 79 | ] 80 | if self.val[name][0] < 0: self.val[name][0] += maxint + 1 81 | if self.val[name][1] < 0: self.val[name][1] += maxint + 1 82 | if step == op.delay: 83 | self.set1.update(self.set2) 84 | -------------------------------------------------------------------------------- /plugins/dstat_innodb_buffer.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | global mysql_options 4 | mysql_options = os.getenv('DSTAT_MYSQL') 5 | 6 | class dstat_plugin(dstat): 7 | def __init__(self): 8 | self.name = 'innodb pool' 9 | self.nick = ('crt', 'rea', 'wri') 10 | self.vars = ('created', 'read', 'written') 11 | self.type = 'f' 12 | self.width = 3 13 | self.scale = 1000 14 | 15 | def check(self): 16 | if not os.access('/usr/bin/mysql', os.X_OK): 17 | raise Exception('Needs MySQL binary') 18 | try: 19 | self.stdin, self.stdout, self.stderr = dpopen('/usr/bin/mysql -n %s' % mysql_options) 20 | except IOError as e: 21 | raise Exception('Cannot interface with MySQL binary (%s)' % e) 22 | 23 | def extract(self): 24 | try: 25 | self.stdin.write('show engine innodb status\G\n') 26 | line = greppipe(self.stdout, 'Pages read ') 27 | 28 | if line: 29 | l = line.split() 30 | self.set2['read'] = int(l[2].rstrip(',')) 31 | self.set2['created'] = int(l[4].rstrip(',')) 32 | self.set2['written'] = int(l[6]) 33 | 34 | for name in self.vars: 35 | self.val[name] = (self.set2[name] - self.set1[name]) * 1.0 / elapsed 36 | 37 | if step == op.delay: 38 | self.set1.update(self.set2) 39 | 40 | except IOError as e: 41 | if op.debug > 1: print('%s: lost pipe to mysql, %s' % (self.filename, e)) 42 | for name in self.vars: self.val[name] = -1 43 | 44 | except Exception as e: 45 | if op.debug > 1: print('%s: exception: %s' % (self.filename, e)) 46 | for name in self.vars: self.val[name] = -1 47 | 48 | # vim:ts=4:sw=4:et 49 | -------------------------------------------------------------------------------- /plugins/dstat_innodb_io.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | global mysql_options 4 | mysql_options = os.getenv('DSTAT_MYSQL') 5 | 6 | class dstat_plugin(dstat): 7 | def __init__(self): 8 | self.name = 'innodb io ops ' 9 | self.nick = ('rea', 'wri', 'syn') 10 | self.vars = ('read', 'write', 'sync') 11 | self.type = 'f' 12 | self.width = 3 13 | self.scale = 1000 14 | 15 | def check(self): 16 | if os.access('/usr/bin/mysql', os.X_OK): 17 | try: 18 | self.stdin, self.stdout, self.stderr = dpopen('/usr/bin/mysql -n %s' % mysql_options) 19 | except IOError: 20 | raise Exception('Cannot interface with MySQL binary') 21 | return True 22 | raise Exception('Needs MySQL binary') 23 | 24 | def extract(self): 25 | try: 26 | self.stdin.write('show engine innodb status\G\n') 27 | line = matchpipe(self.stdout, '.*OS file reads,.*') 28 | 29 | if line: 30 | l = line.split() 31 | self.set2['read'] = int(l[0]) 32 | self.set2['write'] = int(l[4]) 33 | self.set2['sync'] = int(l[8]) 34 | 35 | for name in self.vars: 36 | self.val[name] = (self.set2[name] - self.set1[name]) * 1.0 / elapsed 37 | 38 | if step == op.delay: 39 | self.set1.update(self.set2) 40 | 41 | except IOError as e: 42 | if op.debug > 1: print('%s: lost pipe to mysql, %s' % (self.filename, e)) 43 | for name in self.vars: self.val[name] = -1 44 | 45 | except Exception as e: 46 | if op.debug > 1: print('%s: exception' % (self.filename, e)) 47 | for name in self.vars: self.val[name] = -1 48 | 49 | # vim:ts=4:sw=4:et 50 | -------------------------------------------------------------------------------- /plugins/dstat_innodb_ops.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | global mysql_options 4 | mysql_options = os.getenv('DSTAT_MYSQL') 5 | 6 | class dstat_plugin(dstat): 7 | def __init__(self): 8 | self.name = 'innodb ops' 9 | self.nick = ('ins', 'upd', 'del', 'rea') 10 | self.vars = ('inserted', 'updated', 'deleted', 'read') 11 | self.type = 'f' 12 | self.width = 3 13 | self.scale = 1000 14 | 15 | def check(self): 16 | if os.access('/usr/bin/mysql', os.X_OK): 17 | try: 18 | self.stdin, self.stdout, self.stderr = dpopen('/usr/bin/mysql -n %s' % mysql_options) 19 | except IOError: 20 | raise Exception('Cannot interface with MySQL binary') 21 | return True 22 | raise Exception('Needs MySQL binary') 23 | 24 | def extract(self): 25 | try: 26 | self.stdin.write('show engine innodb status\G\n') 27 | line = greppipe(self.stdout, 'Number of rows inserted') 28 | 29 | if line: 30 | l = line.split() 31 | self.set2['inserted'] = int(l[4].rstrip(',')) 32 | self.set2['updated'] = int(l[6].rstrip(',')) 33 | self.set2['deleted'] = int(l[8].rstrip(',')) 34 | self.set2['read'] = int(l[10]) 35 | 36 | for name in self.vars: 37 | self.val[name] = (self.set2[name] - self.set1[name]) * 1.0 / elapsed 38 | 39 | if step == op.delay: 40 | self.set1.update(self.set2) 41 | 42 | except IOError as e: 43 | if op.debug > 1: print('%s: lost pipe to mysql, %s' % (self.filename, e)) 44 | for name in self.vars: self.val[name] = -1 45 | 46 | except Exception as e: 47 | if op.debug > 1: print('%s: exception' % (self.filename, e)) 48 | for name in self.vars: self.val[name] = -1 49 | 50 | # vim:ts=4:sw=4:et 51 | -------------------------------------------------------------------------------- /plugins/dstat_jvm_vm.py: -------------------------------------------------------------------------------- 1 | # Author: Roberto Polli 2 | # 3 | # This plugin shows jvm stats using the JVM_PID environment variable. 4 | # Requires the presence of the /tmp/hsperfdata_* directory and 5 | # files created when running java with the profiler enabled. 6 | # 7 | 8 | 9 | class dstat_plugin(dstat): 10 | 11 | def __init__(self): 12 | self.name = 'jvm mem ops ' 13 | self.vars = ('fgc', 'heap', 'heap%', 'perm', 'perm%') 14 | self.type = 'f' 15 | self.width = 5 16 | self.scale = 1000 17 | 18 | def check(self): 19 | if not os.access('/usr/bin/jstat', os.X_OK): 20 | raise Exception('Needs jstat binary') 21 | try: 22 | self.jvm_pid = int(os.environ.get('JVM_PID', 0)) 23 | except Exception: 24 | self.jvm_pid = 0 25 | 26 | return True 27 | 28 | @staticmethod 29 | def _to_float(s): 30 | return float(s.replace(",", ".")) 31 | 32 | @staticmethod 33 | def _cmd_splitlines(cmd): 34 | for l in os.popen(cmd): 35 | yield l.strip().split() 36 | 37 | def extract(self): 38 | from collections import namedtuple 39 | try: 40 | lines = self._cmd_splitlines( 41 | '/usr/bin/jstat -gc %s' % self.jvm_pid) 42 | headers = next(lines) 43 | DStatParser = namedtuple('DStatParser', headers) 44 | line = next(lines) 45 | if line: 46 | stats = DStatParser(*[self._to_float(x) for x in line]) 47 | # print(stats) 48 | self.set2['cls'] = 0 49 | self.set2['fgc'] = int(stats.FGC) 50 | self.set2['heap'] = ( 51 | stats.S0C + stats.S1C + stats.EC + stats.OC) 52 | self.set2['heapu'] = ( 53 | stats.S0U + stats.S1U + stats.EU + stats.OU) 54 | 55 | # Use MetaSpace on jdk8 56 | try: 57 | self.set2['perm'] = stats.PC 58 | self.set2['permu'] = stats.PU 59 | except AttributeError: 60 | self.set2['perm'] = stats.MC 61 | self.set2['permu'] = stats.MU 62 | 63 | # Evaluate statistics on memory usage. 64 | for name in ('heap', 'perm'): 65 | self.set2[name + '%'] = 100 * self.set2[ 66 | name + 'u'] / self.set2[name] 67 | self.set2[name] /= 1024 68 | 69 | for name in self.vars: 70 | self.val[name] = self.set2[name] 71 | 72 | if step == op.delay: 73 | self.set1.update(self.set2) 74 | 75 | except IOError as e: 76 | if op.debug > 1: 77 | print('%s: lost pipe to jstat, %s' % (self.filename, e)) 78 | for name in self.vars: 79 | self.val[name] = -1 80 | 81 | except Exception as e: 82 | if op.debug > 1: 83 | print('%s: exception' % e) 84 | for name in self.vars: 85 | self.val[name] = -1 86 | 87 | # vim:ts=4:sw=4:et 88 | -------------------------------------------------------------------------------- /plugins/dstat_lustre.py: -------------------------------------------------------------------------------- 1 | # Author: Brock Palen , Kilian Vavalotti 2 | 3 | class dstat_plugin(dstat): 4 | def __init__(self): 5 | self.nick = ('read', 'write') 6 | self.cols = 2 7 | 8 | def check(self): 9 | if not os.path.exists('/proc/fs/lustre/llite'): 10 | raise Exception('Lustre filesystem not found') 11 | info(1, 'Module %s is still experimental.' % self.filename) 12 | 13 | def name(self): 14 | return [mount for mount in os.listdir('/proc/fs/lustre/llite')] 15 | 16 | def vars(self): 17 | return [mount for mount in os.listdir('/proc/fs/lustre/llite')] 18 | 19 | def extract(self): 20 | for name in self.vars: 21 | for line in dopen(os.path.join('/proc/fs/lustre/llite', name, 'stats')).readlines(): 22 | l = line.split() 23 | if len(l) < 6: continue 24 | if l[0] == 'read_bytes': 25 | read = int(l[6]) 26 | elif l[0] == 'write_bytes': 27 | write = int(l[6]) 28 | self.set2[name] = (read, write) 29 | 30 | self.val[name] = list(map(lambda x, y: (y - x) * 1.0 / elapsed, self.set1[name], self.set2[name])) 31 | 32 | if step == op.delay: 33 | self.set1.update(self.set2) 34 | 35 | # vim:ts=4:sw=4 36 | -------------------------------------------------------------------------------- /plugins/dstat_md_status.py: -------------------------------------------------------------------------------- 1 | ### Author: Bert de Bruijn 2 | 3 | class dstat_plugin(dstat): 4 | """ 5 | Recovery state of software RAID rebuild. 6 | 7 | Prints completed recovery percentage and rebuild speed of the md device 8 | that is actively being recovered or resynced. 9 | 10 | If no devices are being rebuilt, it displays 100%, 0B. If instead 11 | multiple devices are being rebuilt, it displays the total progress 12 | and total throughput. 13 | """ 14 | 15 | def __init__(self): 16 | self.name = 'sw raid' 17 | self.type = 's' 18 | self.scale = 0 19 | self.nick = ('pct speed', ) 20 | self.width = 9 21 | self.vars = ('text', ) 22 | self.open('/proc/mdstat') 23 | 24 | def check(self): 25 | if not os.path.exists('/proc/mdstat'): 26 | raise Exception('Needs kernel md support') 27 | 28 | def extract(self): 29 | pct = 0 30 | speed = 0 31 | nr = 0 32 | for l in self.splitlines(): 33 | if len(l) < 2: continue 34 | if l[1] in ('recovery', 'reshape', 'resync'): 35 | nr += 1 36 | pct += int(l[3][0:2].strip('.%')) 37 | speed += int(l[6].strip('sped=K/sc')) * 1024 38 | if nr: 39 | pct = pct / nr 40 | else: 41 | pct = 100 42 | self.val['text'] = '%s %s' % (cprint(pct, 'p', 3, 34), cprint(speed, 'd', 5, 1024)) 43 | 44 | # vim:ts=4:sw=4:et 45 | -------------------------------------------------------------------------------- /plugins/dstat_memcache_hits.py: -------------------------------------------------------------------------------- 1 | ### Author: Dean Wilson 2 | 3 | class dstat_plugin(dstat): 4 | """ 5 | Memcache hit count plugin. 6 | 7 | Displays the number of memcache get_hits and get_misses. 8 | """ 9 | def __init__(self): 10 | self.name = 'Memcache Hits' 11 | self.nick = ('Hit', 'Miss') 12 | self.vars = ('get_hits', 'get_misses') 13 | self.type = 'd' 14 | self.width = 6 15 | self.scale = 50 16 | 17 | def check(self): 18 | try: 19 | global memcache 20 | import memcache 21 | self.mc = memcache.Client(['127.0.0.1:11211'], debug=0) 22 | except: 23 | raise Exception('Plugin needs the memcache module') 24 | 25 | def extract(self): 26 | stats = self.mc.get_stats() 27 | for key in self.vars: 28 | self.val[key] = int(stats[0][1][key]) 29 | -------------------------------------------------------------------------------- /plugins/dstat_mongodb_conn.py: -------------------------------------------------------------------------------- 1 | ### Author: 2 | 3 | global mongodb_user 4 | mongodb_user = os.getenv('DSTAT_MONGODB_USER') or os.getenv('USER') 5 | 6 | global mongodb_pwd 7 | mongodb_pwd = os.getenv('DSTAT_MONGODB_PWD') 8 | 9 | global mongodb_host 10 | mongodb_host = os.getenv('DSTAT_MONGODB_HOST') or '127.0.0.1:27017' 11 | 12 | class dstat_plugin(dstat): 13 | """ 14 | Plugin for MongoDB. 15 | """ 16 | def __init__(self): 17 | global pymongo 18 | import pymongo 19 | 20 | try: 21 | self.m = pymongo.MongoClient(mongodb_host) 22 | if mongodb_pwd: 23 | self.m.admin.authenticate(mongodb_user, mongodb_pwd) 24 | self.db = self.m.admin 25 | except Exception as e: 26 | raise Exception('Cannot interface with MongoDB server: %s' % e) 27 | 28 | self.name = 'mongodb con' 29 | self.nick = ('curr', 'avail') 30 | self.vars = ('connections.current', 'connections.available') 31 | self.type = 'd' 32 | self.width = 5 33 | self.scale = 2 34 | self.lastVal = {} 35 | 36 | def extract(self): 37 | status = self.db.command("serverStatus") 38 | 39 | for name in self.vars: 40 | self.val[name] = (int(self.getDoc(status, name))) 41 | 42 | def getDoc(self, dic, doc): 43 | par = doc.split('.') 44 | sdic = dic 45 | for p in par: 46 | sdic = sdic.get(p) 47 | 48 | return sdic 49 | -------------------------------------------------------------------------------- /plugins/dstat_mongodb_mem.py: -------------------------------------------------------------------------------- 1 | ### Author: 2 | 3 | global mongodb_user 4 | mongodb_user = os.getenv('DSTAT_MONGODB_USER') or os.getenv('USER') 5 | 6 | global mongodb_pwd 7 | mongodb_pwd = os.getenv('DSTAT_MONGODB_PWD') 8 | 9 | global mongodb_host 10 | mongodb_host = os.getenv('DSTAT_MONGODB_HOST') or '127.0.0.1:27017' 11 | 12 | class dstat_plugin(dstat): 13 | """ 14 | Plugin for MongoDB. 15 | """ 16 | def __init__(self): 17 | global pymongo 18 | import pymongo 19 | 20 | try: 21 | self.m = pymongo.MongoClient(mongodb_host) 22 | if mongodb_pwd: 23 | self.m.admin.authenticate(mongodb_user, mongodb_pwd) 24 | self.db = self.m.admin 25 | except Exception as e: 26 | raise Exception('Cannot interface with MongoDB server: %s' % e) 27 | 28 | line = self.db.command("serverStatus") 29 | if 'storageEngine' in line: 30 | self.storageEngine = line.get('storageEngine').get('name') 31 | else: 32 | self.storageEngine = 'mmapv1' 33 | 34 | self.name = 'mongodb mem' 35 | self.nick = ('res', 'virt') 36 | self.vars = ('mem.resident', 'mem.virtual') 37 | self.type = 'd' 38 | self.width = 5 39 | self.scale = 2 40 | self.lastVal = {} 41 | 42 | if self.storageEngine == 'mmapv1': 43 | self.nick = self.nick + ('map', 'mapj', 'flt') 44 | self.vars = self.vars + ('mem.mapped', 'mem.mappedWithJournal', 'extra_info.page_faults') 45 | 46 | 47 | def extract(self): 48 | status = self.db.command("serverStatus") 49 | 50 | for name in self.vars: 51 | if name in ('extra_info.page_faults'): 52 | if not name in self.lastVal: 53 | self.lastVal[name] = int(self.getDoc(status, name)) 54 | self.val[name] = (int(self.getDoc(status, name)) - self.lastVal[name]) 55 | self.lastVal[name] = self.getDoc(status, name) 56 | else: 57 | self.val[name] = (int(self.getDoc(status, name))) 58 | 59 | 60 | 61 | def getDoc(self, dic, doc): 62 | par = doc.split('.') 63 | sdic = dic 64 | for p in par: 65 | sdic = sdic.get(p) 66 | 67 | return sdic 68 | -------------------------------------------------------------------------------- /plugins/dstat_mongodb_opcount.py: -------------------------------------------------------------------------------- 1 | ### Author: 2 | 3 | global mongodb_user 4 | mongodb_user = os.getenv('DSTAT_MONGODB_USER') or os.getenv('USER') 5 | 6 | global mongodb_pwd 7 | mongodb_pwd = os.getenv('DSTAT_MONGODB_PWD') 8 | 9 | global mongodb_host 10 | mongodb_host = os.getenv('DSTAT_MONGODB_HOST') or '127.0.0.1:27017' 11 | 12 | class dstat_plugin(dstat): 13 | """ 14 | Plugin for MongoDB. 15 | """ 16 | def __init__(self): 17 | global pymongo 18 | import pymongo 19 | 20 | try: 21 | self.m = pymongo.MongoClient(mongodb_host) 22 | if mongodb_pwd: 23 | self.m.admin.authenticate(mongodb_user, mongodb_pwd) 24 | self.db = self.m.admin 25 | except Exception as e: 26 | raise Exception('Cannot interface with MongoDB server: %s' % e) 27 | 28 | self.name = 'mongodb counts' 29 | self.nick = ('qry', 'ins', 'upd', 'del', 'gtm', 'cmd') 30 | self.vars = ('query', 'insert','update','delete','getmore','command') 31 | self.type = 'd' 32 | self.width = 5 33 | self.scale = 2 34 | self.lastVal = {} 35 | 36 | def extract(self): 37 | status = self.db.command("serverStatus") 38 | opct = status['opcounters'] 39 | 40 | for name in self.vars: 41 | if name in opct.iterkeys(): 42 | if not name in self.lastVal: 43 | self.lastVal[name] = opct.get(name) 44 | 45 | self.val[name] = (int(opct.get(name)) - self.lastVal[name]) / elapsed 46 | self.lastVal[name] = opct.get(name) 47 | -------------------------------------------------------------------------------- /plugins/dstat_mongodb_queue.py: -------------------------------------------------------------------------------- 1 | ### Author: 2 | 3 | global mongodb_user 4 | mongodb_user = os.getenv('DSTAT_MONGODB_USER') or os.getenv('USER') 5 | 6 | global mongodb_pwd 7 | mongodb_pwd = os.getenv('DSTAT_MONGODB_PWD') 8 | 9 | global mongodb_host 10 | mongodb_host = os.getenv('DSTAT_MONGODB_HOST') or '127.0.0.1:27017' 11 | 12 | class dstat_plugin(dstat): 13 | """ 14 | Plugin for MongoDB. 15 | """ 16 | def __init__(self): 17 | global pymongo 18 | import pymongo 19 | 20 | try: 21 | self.m = pymongo.MongoClient(mongodb_host) 22 | if mongodb_pwd: 23 | self.m.admin.authenticate(mongodb_user, mongodb_pwd) 24 | self.db = self.m.admin 25 | except Exception as e: 26 | raise Exception('Cannot interface with MongoDB server: %s' % e) 27 | 28 | self.name = 'mongodb queues' 29 | self.nick = ('ar', 'aw', 'qt', 'qw') 30 | self.vars = ('ar', 'aw', 'qt', 'qw') 31 | self.type = 'd' 32 | self.width = 5 33 | self.scale = 2 34 | self.lastVal = {} 35 | 36 | def extract(self): 37 | status = self.db.command("serverStatus") 38 | glock = status['globalLock'] 39 | alock = glock['activeClients'] 40 | qlock = glock['currentQueue'] 41 | 42 | self.val['ar'] = int(alock['readers']) 43 | self.val['aw'] = int(alock['writers']) 44 | self.val['qr'] = int(qlock['readers']) 45 | self.val['qw'] = int(qlock['writers']) 46 | -------------------------------------------------------------------------------- /plugins/dstat_mongodb_stats.py: -------------------------------------------------------------------------------- 1 | ### Author: 2 | 3 | global mongodb_user 4 | mongodb_user = os.getenv('DSTAT_MONGODB_USER') or os.getenv('USER') 5 | 6 | global mongodb_pwd 7 | mongodb_pwd = os.getenv('DSTAT_MONGODB_PWD') 8 | 9 | global mongodb_host 10 | mongodb_host = os.getenv('DSTAT_MONGODB_HOST') or '127.0.0.1:27017' 11 | 12 | class dstat_plugin(dstat): 13 | """ 14 | Plugin for MongoDB. 15 | """ 16 | def __init__(self): 17 | global pymongo 18 | import pymongo 19 | 20 | try: 21 | self.m = pymongo.MongoClient(mongodb_host) 22 | if mongodb_pwd: 23 | self.m.admin.authenticate(mongodb_user, mongodb_pwd) 24 | self.db = self.m.admin 25 | except Exception as e: 26 | raise Exception('Cannot interface with MongoDB server: %s' % e) 27 | 28 | stats = self.db.command("listDatabases") 29 | self.dbList = [] 30 | for db in stats.get('databases'): 31 | self.dbList.append(db.get('name')) 32 | 33 | line = self.db.command("serverStatus") 34 | if 'storageEngine' in line: 35 | self.storageEngine = line.get('storageEngine').get('name') 36 | else: 37 | self.storageEngine = 'mmapv1' 38 | 39 | self.name = 'mongodb stats' 40 | self.nick = ('dsize', 'isize', 'ssize') 41 | self.vars = ('dataSize', 'indexSize', 'storageSize') 42 | self.type = 'b' 43 | self.width = 5 44 | self.scale = 2 45 | self.count = 1 46 | 47 | if self.storageEngine == 'mmapv1': 48 | self.nick = self.nick + ('fsize',) 49 | self.vars = self.vars + ('fileSize',) 50 | 51 | 52 | def extract(self): 53 | self.set = {} 54 | 55 | # refresh the database list every 10 iterations 56 | if (self.count % 10) == 0: 57 | stats = self.m.admin.command("listDatabases") 58 | self.dbList = [] 59 | for db in stats.get('databases'): 60 | self.dbList.append(db.get('name')) 61 | self.count += 1 62 | 63 | for name in self.vars: 64 | self.set[name] = 0 65 | 66 | for db in self.dbList: 67 | self.db = self.m.get_database(db) 68 | stats = self.db.command("dbStats") 69 | for name in self.vars: 70 | self.set[name] += int(stats.get(name)) / (1024 * 1024) 71 | self.val = self.set 72 | -------------------------------------------------------------------------------- /plugins/dstat_mysql5_cmds.py: -------------------------------------------------------------------------------- 1 | ### Author: 2 | 3 | global mysql_user 4 | mysql_user = os.getenv('DSTAT_MYSQL_USER') or os.getenv('USER') 5 | 6 | global mysql_pwd 7 | mysql_pwd = os.getenv('DSTAT_MYSQL_PWD') 8 | 9 | global mysql_host 10 | mysql_host = os.getenv('DSTAT_MYSQL_HOST') 11 | 12 | global mysql_port 13 | mysql_port = os.getenv('DSTAT_MYSQL_PORT') 14 | 15 | global mysql_socket 16 | mysql_socket = os.getenv('DSTAT_MYSQL_SOCKET') 17 | 18 | class dstat_plugin(dstat): 19 | """ 20 | Plugin for MySQL 5 commands. 21 | """ 22 | def __init__(self): 23 | self.name = 'mysql5 cmds' 24 | self.nick = ('sel', 'ins','upd','del') 25 | self.vars = ('Com_select', 'Com_insert','Com_update','Com_delete') 26 | self.type = 'd' 27 | self.width = 5 28 | self.scale = 1 29 | 30 | def check(self): 31 | global MySQLdb 32 | import MySQLdb 33 | try: 34 | args = {} 35 | if mysql_user: 36 | args['user'] = mysql_user 37 | if mysql_pwd: 38 | args['passwd'] = mysql_pwd 39 | if mysql_host: 40 | args['host'] = mysql_host 41 | if mysql_port: 42 | args['port'] = mysql_port 43 | if mysql_socket: 44 | args['unix_socket'] = mysql_socket 45 | 46 | self.db = MySQLdb.connect(**args) 47 | except Exception as e: 48 | raise Exception('Cannot interface with MySQL server: %s' % e) 49 | 50 | def extract(self): 51 | try: 52 | c = self.db.cursor() 53 | for name in self.vars: 54 | c.execute("""show global status like '%s';""" % name) 55 | line = c.fetchone() 56 | if line[0] in self.vars: 57 | if line[0] + 'raw' in self.set2: 58 | self.set2[line[0]] = int(line[1]) - self.set2[line[0] + 'raw'] 59 | self.set2[line[0] + 'raw'] = int(line[1]) 60 | 61 | for name in self.vars: 62 | self.val[name] = self.set2[name] * 1.0 / elapsed 63 | 64 | if step == op.delay: 65 | self.set1.update(self.set2) 66 | 67 | except Exception as e: 68 | for name in self.vars: 69 | self.val[name] = -1 70 | -------------------------------------------------------------------------------- /plugins/dstat_mysql5_conn.py: -------------------------------------------------------------------------------- 1 | ### Author: 2 | 3 | global mysql_user 4 | mysql_user = os.getenv('DSTAT_MYSQL_USER') or os.getenv('USER') 5 | 6 | global mysql_pwd 7 | mysql_pwd = os.getenv('DSTAT_MYSQL_PWD') 8 | 9 | global mysql_host 10 | mysql_host = os.getenv('DSTAT_MYSQL_HOST') 11 | 12 | global mysql_port 13 | mysql_port = os.getenv('DSTAT_MYSQL_PORT') 14 | 15 | global mysql_socket 16 | mysql_socket = os.getenv('DSTAT_MYSQL_SOCKET') 17 | 18 | class dstat_plugin(dstat): 19 | """ 20 | Plugin for MySQL 5 connections. 21 | """ 22 | 23 | def __init__(self): 24 | self.name = 'mysql5 conn' 25 | self.nick = ('ThCon', '%Con') 26 | self.vars = ('Threads_connected', 'Threads') 27 | self.type = 'f' 28 | self.width = 4 29 | self.scale = 1 30 | 31 | def check(self): 32 | global MySQLdb 33 | import MySQLdb 34 | try: 35 | args = {} 36 | if mysql_user: 37 | args['user'] = mysql_user 38 | if mysql_pwd: 39 | args['passwd'] = mysql_pwd 40 | if mysql_host: 41 | args['host'] = mysql_host 42 | if mysql_port: 43 | args['port'] = mysql_port 44 | if mysql_socket: 45 | args['unix_socket'] = mysql_socket 46 | 47 | self.db = MySQLdb.connect(**args) 48 | except Exception as e: 49 | raise Exception('Cannot interface with MySQL server, %s' % e) 50 | 51 | def extract(self): 52 | try: 53 | c = self.db.cursor() 54 | c.execute("""show global variables like 'max_connections';""") 55 | max = c.fetchone() 56 | c.execute("""show global status like 'Threads_connected';""") 57 | thread = c.fetchone() 58 | if thread[0] in self.vars: 59 | self.set2[thread[0]] = float(thread[1]) 60 | self.set2['Threads'] = float(thread[1]) / float(max[1]) * 100.0 61 | 62 | for name in self.vars: 63 | self.val[name] = self.set2[name] * 1.0 / elapsed 64 | 65 | if step == op.delay: 66 | self.set1.update(self.set2) 67 | 68 | except Exception as e: 69 | for name in self.vars: 70 | self.val[name] = -1 71 | 72 | # vim:ts=4:sw=4:et 73 | -------------------------------------------------------------------------------- /plugins/dstat_mysql5_innodb_basic.py: -------------------------------------------------------------------------------- 1 | dstat_mysql5_innodb.py -------------------------------------------------------------------------------- /plugins/dstat_mysql5_innodb_extra.py: -------------------------------------------------------------------------------- 1 | dstat_mysql5_innodb.py -------------------------------------------------------------------------------- /plugins/dstat_mysql5_io.py: -------------------------------------------------------------------------------- 1 | ### Author: 2 | 3 | global mysql_user 4 | mysql_user = os.getenv('DSTAT_MYSQL_USER') or os.getenv('USER') 5 | 6 | global mysql_pwd 7 | mysql_pwd = os.getenv('DSTAT_MYSQL_PWD') 8 | 9 | global mysql_host 10 | mysql_host = os.getenv('DSTAT_MYSQL_HOST') 11 | 12 | global mysql_port 13 | mysql_port = os.getenv('DSTAT_MYSQL_PORT') 14 | 15 | global mysql_socket 16 | mysql_socket = os.getenv('DSTAT_MYSQL_SOCKET') 17 | 18 | class dstat_plugin(dstat): 19 | """ 20 | Plugin for MySQL 5 I/O. 21 | """ 22 | 23 | def __init__(self): 24 | self.name = 'mysql5 io' 25 | self.nick = ('recv', 'sent') 26 | self.vars = ('Bytes_received', 'Bytes_sent') 27 | 28 | def check(self): 29 | global MySQLdb 30 | import MySQLdb 31 | try: 32 | args = {} 33 | if mysql_user: 34 | args['user'] = mysql_user 35 | if mysql_pwd: 36 | args['passwd'] = mysql_pwd 37 | if mysql_host: 38 | args['host'] = mysql_host 39 | if mysql_port: 40 | args['port'] = mysql_port 41 | if mysql_socket: 42 | args['unix_socket'] = mysql_socket 43 | 44 | self.db = MySQLdb.connect(**args) 45 | except: 46 | raise Exception('Cannot interface with MySQL server') 47 | 48 | def extract(self): 49 | try: 50 | c = self.db.cursor() 51 | c.execute("""show global status like 'Bytes_%';""") 52 | lines = c.fetchall() 53 | for line in lines: 54 | if len(line[1]) < 2: continue 55 | if line[0] in self.vars: 56 | if line[0] + 'raw' in self.set2: 57 | self.set2[line[0]] = float(line[1]) - self.set2[line[0] + 'raw'] 58 | self.set2[line[0] + 'raw'] = float(line[1]) 59 | 60 | for name in self.vars: 61 | self.val[name] = self.set2[name] * 1.0 / elapsed 62 | 63 | if step == op.delay: 64 | self.set1.update(self.set2) 65 | 66 | except Exception as e: 67 | for name in self.vars: 68 | self.val[name] = -1 69 | 70 | # vim:ts=4:sw=4:et 71 | -------------------------------------------------------------------------------- /plugins/dstat_mysql5_keys.py: -------------------------------------------------------------------------------- 1 | ### Author: 2 | 3 | global mysql_user 4 | mysql_user = os.getenv('DSTAT_MYSQL_USER') or os.getenv('USER') 5 | 6 | global mysql_pwd 7 | mysql_pwd = os.getenv('DSTAT_MYSQL_PWD') 8 | 9 | global mysql_host 10 | mysql_host = os.getenv('DSTAT_MYSQL_HOST') 11 | 12 | global mysql_port 13 | mysql_port = os.getenv('DSTAT_MYSQL_PORT') 14 | 15 | global mysql_socket 16 | mysql_socket = os.getenv('DSTAT_MYSQL_SOCKET') 17 | 18 | class dstat_plugin(dstat): 19 | """ 20 | Plugin for MySQL 5 Keys. 21 | """ 22 | 23 | def __init__(self): 24 | self.name = 'mysql5 key status' 25 | self.nick = ('used', 'read', 'writ', 'rreq', 'wreq') 26 | self.vars = ('Key_blocks_used', 'Key_reads', 'Key_writes', 'Key_read_requests', 'Key_write_requests') 27 | self.type = 'f' 28 | self.width = 4 29 | self.scale = 1000 30 | 31 | def check(self): 32 | global MySQLdb 33 | import MySQLdb 34 | try: 35 | args = {} 36 | if mysql_user: 37 | args['user'] = mysql_user 38 | if mysql_pwd: 39 | args['passwd'] = mysql_pwd 40 | if mysql_host: 41 | args['host'] = mysql_host 42 | if mysql_port: 43 | args['port'] = mysql_port 44 | if mysql_socket: 45 | args['unix_socket'] = mysql_socket 46 | 47 | self.db = MySQLdb.connect(**args) 48 | except: 49 | raise Exception('Cannot interface with MySQL server') 50 | 51 | def extract(self): 52 | try: 53 | c = self.db.cursor() 54 | c.execute("""show global status like 'Key_%';""") 55 | lines = c.fetchall() 56 | for line in lines: 57 | if len(line[1]) < 2: continue 58 | if line[0] in self.vars: 59 | self.set2[line[0]] = float(line[1]) 60 | 61 | for name in self.vars: 62 | self.val[name] = self.set2[name] * 1.0 / elapsed 63 | 64 | if step == op.delay: 65 | self.set1.update(self.set2) 66 | 67 | except Exception as e: 68 | for name in self.vars: 69 | self.val[name] = -1 70 | 71 | # vim:ts=4:sw=4:et 72 | -------------------------------------------------------------------------------- /plugins/dstat_mysql_io.py: -------------------------------------------------------------------------------- 1 | global mysql_options 2 | mysql_options = os.getenv('DSTAT_MYSQL') 3 | 4 | class dstat_plugin(dstat): 5 | def __init__(self): 6 | self.name = 'mysql io' 7 | self.nick = ('recv', 'sent') 8 | self.vars = ('Bytes_received', 'Bytes_sent') 9 | 10 | def check(self): 11 | if not os.access('/usr/bin/mysql', os.X_OK): 12 | raise Exception('Needs MySQL binary') 13 | try: 14 | self.stdin, self.stdout, self.stderr = dpopen('/usr/bin/mysql -n %s' % mysql_options) 15 | except IOError: 16 | raise Exception('Cannot interface with MySQL binary') 17 | 18 | def extract(self): 19 | try: 20 | self.stdin.write("show status like 'Bytes_%';\n") 21 | for line in readpipe(self.stdout): 22 | l = line.split() 23 | if len(l) < 2: continue 24 | if l[0] in self.vars: 25 | self.set2[l[0]] = float(l[1]) 26 | 27 | for name in self.vars: 28 | self.val[name] = (self.set2[name] - self.set1[name]) * 1.0 / elapsed 29 | 30 | if step == op.delay: 31 | self.set1.update(self.set2) 32 | 33 | except IOError as e: 34 | if op.debug > 1: print('%s: lost pipe to mysql, %s' % (self.filename, e)) 35 | for name in self.vars: self.val[name] = -1 36 | 37 | except Exception as e: 38 | if op.debug > 1: print('dstat_innodb_buffer: exception', e) 39 | for name in self.vars: self.val[name] = -1 40 | 41 | # vim:ts=4:sw=4:et 42 | -------------------------------------------------------------------------------- /plugins/dstat_mysql_keys.py: -------------------------------------------------------------------------------- 1 | global mysql_options 2 | mysql_options = os.getenv('DSTAT_MYSQL') 3 | 4 | class dstat_plugin(dstat): 5 | def __init__(self): 6 | self.name = 'mysql key status' 7 | self.nick = ('used', 'read', 'writ', 'rreq', 'wreq') 8 | self.vars = ('Key_blocks_used', 'Key_reads', 'Key_writes', 'Key_read_requests', 'Key_write_requests') 9 | self.type = 'f' 10 | self.width = 4 11 | self.scale = 1000 12 | 13 | def check(self): 14 | if not os.access('/usr/bin/mysql', os.X_OK): 15 | raise Exception('Needs MySQL binary') 16 | try: 17 | self.stdin, self.stdout, self.stderr = dpopen('/usr/bin/mysql -n %s' % mysql_options) 18 | except IOError: 19 | raise Exception('Cannot interface with MySQL binary') 20 | 21 | def extract(self): 22 | try: 23 | self.stdin.write("show status like 'Key_%';\n") 24 | for line in readpipe(self.stdout): 25 | l = line.split() 26 | if len(l) < 2: continue 27 | if l[0] in self.vars: 28 | self.set2[l[0]] = float(l[1]) 29 | 30 | for name in self.vars: 31 | self.val[name] = (self.set2[name] - self.set1[name]) * 1.0 / elapsed 32 | 33 | if step == op.delay: 34 | self.set1.update(self.set2) 35 | 36 | except IOError as e: 37 | if op.debug > 1: print('%s: lost pipe to mysql, %s' % (self.filename, e)) 38 | for name in self.vars: self.val[name] = -1 39 | 40 | except Exception as e: 41 | if op.debug > 1: print('%s: exception' (self.filename, e)) 42 | for name in self.vars: self.val[name] = -1 43 | 44 | # vim:ts=4:sw=4:et 45 | -------------------------------------------------------------------------------- /plugins/dstat_net_packets.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | """ 5 | Number of packets received and send per interface. 6 | """ 7 | 8 | def __init__(self): 9 | self.nick = ('#recv', '#send') 10 | self.type = 'd' 11 | self.width = 5 12 | self.scale = 1000 13 | self.totalfilter = re.compile('^(lo|bond\d+|face|.+\.\d+)$') 14 | self.open('/proc/net/dev') 15 | self.cols = 2 16 | 17 | def discover(self, *objlist): 18 | ret = [] 19 | for l in self.splitlines(replace=':'): 20 | if len(l) < 17: continue 21 | if l[2] == '0' and l[10] == '0': continue 22 | name = l[0] 23 | if name not in ('lo', 'face'): 24 | ret.append(name) 25 | ret.sort() 26 | for item in objlist: ret.append(item) 27 | return ret 28 | 29 | def vars(self): 30 | ret = [] 31 | if op.netlist: 32 | varlist = op.netlist 33 | elif not op.full: 34 | varlist = ('total',) 35 | else: 36 | varlist = self.discover 37 | # if len(varlist) > 2: varlist = varlist[0:2] 38 | varlist.sort() 39 | for name in varlist: 40 | if name in self.discover + ['total', 'lo']: 41 | ret.append(name) 42 | if not ret: 43 | raise Exception('No suitable network interfaces found to monitor') 44 | return ret 45 | 46 | def name(self): 47 | return ['pkt/'+name for name in self.vars] 48 | 49 | def extract(self): 50 | self.set2['total'] = [0, 0] 51 | for l in self.splitlines(replace=':'): 52 | if len(l) < 17: continue 53 | if l[2] == '0' and l[10] == '0': continue 54 | name = l[0] 55 | if name in self.vars : 56 | self.set2[name] = ( int(l[2]), int(l[10]) ) 57 | if not self.totalfilter.match(name): 58 | self.set2['total'] = ( self.set2['total'][0] + int(l[2]), self.set2['total'][1] + int(l[10])) 59 | 60 | if update: 61 | for name in self.set2: 62 | self.val[name] = list(map(lambda x, y: (y - x) * 1.0 / elapsed, self.set1[name], self.set2[name])) 63 | 64 | if step == op.delay: 65 | self.set1.update(self.set2) 66 | -------------------------------------------------------------------------------- /plugins/dstat_nfs3.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | def __init__(self): 5 | self.name = 'nfs3 client' 6 | self.nick = ('read', 'writ', 'rdir', 'othr', 'fs', 'cmmt') 7 | self.vars = ('read', 'write', 'readdir', 'other', 'filesystem', 'commit') 8 | self.type = 'd' 9 | self.width = 5 10 | self.scale = 1000 11 | self.open('/proc/net/rpc/nfs') 12 | 13 | def extract(self): 14 | for l in self.splitlines(): 15 | if not l or l[0] != 'proc3': continue 16 | self.set2['read'] = int(l[8]) 17 | self.set2['write'] = int(l[9]) 18 | self.set2['readdir'] = int(l[18]) + int(l[19]) 19 | self.set2['other'] = int(l[3]) + int(l[4]) + int(l[5]) + int(l[6]) + int(l[7]) + int(l[10]) + int(l[11]) + int(l[12]) + int(l[13]) + int(l[14]) + int(l[15]) + int(l[16]) + int(l[17]) 20 | self.set2['filesystem'] = int(l[20]) + int(l[21]) + int(l[22]) 21 | self.set2['commit'] = int(l[23]) 22 | 23 | for name in self.vars: 24 | self.val[name] = (self.set2[name] - self.set1[name]) * 1.0 / elapsed 25 | 26 | if step == op.delay: 27 | self.set1.update(self.set2) 28 | 29 | # vim:ts=4:sw=4:et 30 | -------------------------------------------------------------------------------- /plugins/dstat_nfs3_ops.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | def __init__(self): 5 | self.name = 'extended nfs3 client operations' 6 | self.nick = ('null', 'gatr', 'satr', 'look', 'aces', 'rdln', 'read', 'writ', 'crea', 'mkdr', 'syml', 'mknd', 'rm', 'rmdr', 'ren', 'link', 'rdir', 'rdr+', 'fstt', 'fsnf', 'path', 'cmmt') 7 | self.vars = ('null', 'getattr', 'setattr', 'lookup', 'access', 'readlink', 'read', 'write', 'create', 'mkdir', 'symlink', 'mknod', 'remove', 'rmdir', 'rename', 'link', 'readdir', 'readdirplus', 'fsstat', 'fsinfo', 'pathconf', 'commit') 8 | self.type = 'd' 9 | self.width = 5 10 | self.scale = 1000 11 | self.open('/proc/net/rpc/nfs') 12 | 13 | def check(self): 14 | info(1, 'Module %s is still experimental.' % self.filename) 15 | 16 | def extract(self): 17 | for l in self.splitlines(): 18 | if not l or l[0] != 'proc3': continue 19 | for i, name in enumerate(self.vars): 20 | self.set2[name] = int(l[i+2]) 21 | 22 | for name in self.vars: 23 | self.val[name] = (self.set2[name] - self.set1[name]) * 1.0 / elapsed 24 | 25 | if step == op.delay: 26 | self.set1.update(self.set2) 27 | 28 | # vim:ts=4:sw=4:et 29 | -------------------------------------------------------------------------------- /plugins/dstat_nfsd3.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | def __init__(self): 5 | self.name = 'nfs3 server' 6 | self.nick = ('read', 'writ', 'rdir', 'inod', 'fs', 'cmmt') 7 | self.vars = ('read', 'write', 'readdir', 'inode', 'filesystem', 'commit') 8 | self.type = 'd' 9 | self.width = 5 10 | self.scale = 1000 11 | self.open('/proc/net/rpc/nfsd') 12 | 13 | def check(self): 14 | info(1, 'Module %s is still experimental.' % self.filename) 15 | 16 | def extract(self): 17 | for l in self.splitlines(): 18 | if not l or l[0] != 'proc3': continue 19 | self.set2['read'] = int(l[8]) 20 | self.set2['write'] = int(l[9]) 21 | self.set2['readdir'] = int(l[18]) + int(l[19]) 22 | self.set2['inode'] = int(l[3]) + int(l[4]) + int(l[5]) + int(l[6]) + int(l[7]) + int(l[10]) + int(l[11]) + int(l[12]) + int(l[13]) + int(l[14]) + int(l[15]) + int(l[16]) + int(l[17]) 23 | self.set2['filesystem'] = int(l[20]) + int(l[21]) + int(l[22]) 24 | self.set2['commit'] = int(l[23]) 25 | 26 | for name in self.vars: 27 | self.val[name] = (self.set2[name] - self.set1[name]) * 1.0 / elapsed 28 | 29 | if step == op.delay: 30 | self.set1.update(self.set2) 31 | 32 | # vim:ts=4:sw=4:et 33 | -------------------------------------------------------------------------------- /plugins/dstat_nfsd3_ops.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | def __init__(self): 5 | self.name = 'extended nfs3 server operations' 6 | self.nick = ('null', 'gatr', 'satr', 'look', 'aces', 'rdln', 'read', 'writ', 'crea', 'mkdr', 'syml', 'mknd', 'rm', 'rmdr', 'ren', 'link', 'rdir', 'rdr+', 'fstt', 'fsnf', 'path', 'cmmt') 7 | self.vars = ('null', 'getattr', 'setattr', 'lookup', 'access', 'readlink', 'read', 'write', 'create', 'mkdir', 'symlink', 'mknod', 'remove', 'rmdir', 'rename', 'link', 'readdir', 'readdirplus', 'fsstat', 'fsinfo', 'pathconf', 'commit') 8 | self.type = 'd' 9 | self.width = 5 10 | self.scale = 1000 11 | self.open('/proc/net/rpc/nfsd') 12 | 13 | def check(self): 14 | info(1, 'Module %s is still experimental.' % self.filename) 15 | 16 | def extract(self): 17 | for l in self.splitlines(): 18 | if not l or l[0] != 'proc3': continue 19 | for i, name in enumerate(self.vars): 20 | self.set2[name] = int(l[i+2]) 21 | 22 | for name in self.vars: 23 | self.val[name] = (self.set2[name] - self.set1[name]) * 1.0 / elapsed 24 | 25 | if step == op.delay: 26 | self.set1.update(self.set2) 27 | 28 | # vim:ts=4:sw=4:et 29 | -------------------------------------------------------------------------------- /plugins/dstat_nfsstat4.py: -------------------------------------------------------------------------------- 1 | ### Author: Adam Michel 2 | ### Based on work by: Dag Wieers 3 | 4 | class dstat_plugin(dstat): 5 | def __init__(self): 6 | self.name = 'nfs4 client' 7 | # this vars/nick pair is the ones I considered relevant. Any set of the full list would work. 8 | self.vars = ('read', 'write', 'readdir', 'commit', 'getattr', 'create', 'link','remove') 9 | self.nick = ('read', 'writ', 'rdir', 'cmmt', 'gatr','crt','link','rmv') 10 | # this is every possible variable if you're into that 11 | #self.vars = ("read", "write", "commit", "open", "open_conf", "open_noat", "open_dgrd", "close", 12 | # "setattr", "fsinfo", "renew", "setclntid", "confirm", "lock", "lockt", "locku", 13 | # "access", "getattr", "lookup", "lookup_root", "remove", "rename", "link", "symlink", 14 | # "create", "pathconf", "statfs", "readlink", "readdir", "server_caps", "delegreturn", 15 | # "getacl", "setacl", "fs_locations", "rel_lkowner", "secinfo") 16 | # these are terrible shortnames for every possible variable 17 | #self.nick = ("read", "writ", "comt", "open", "opnc", "opnn", "opnd", "clse", "seta", "fnfo", 18 | # "renw", "stcd", "cnfm", "lock", "lckt", "lcku", "accs", "gatr", "lkup", "lkp_r", 19 | # "rem", "ren", "lnk", "slnk", "crte", "pthc", "stfs", "rdlk", "rdir", "scps", "delr", 20 | # "gacl", "sacl", "fslo", "relo", "seco") 21 | self.type = 'd' 22 | self.width = 5 23 | self.scale = 1000 24 | self.open('/proc/net/rpc/nfs') 25 | 26 | def check(self): 27 | # other NFS modules had this, so I left it. It seems to work. 28 | info(1, 'Module %s is still experimental.' % self.filename) 29 | 30 | def extract(self): 31 | # list of fields from nfsstat, in order of output from cat /proc/net/rpc/nfs 32 | nfs4_names = ("version", "fieldcount", "null", "read", "write", "commit", "open", "open_conf", 33 | "open_noat", "open_dgrd", "close", "setattr", "fsinfo", "renew", "setclntid", 34 | "confirm", "lock", "lockt", "locku", "access", "getattr", "lookup", "lookup_root", 35 | "remove", "rename", "link", "symlink", "create", "pathconf", "statfs", "readlink", 36 | "readdir", "server_caps", "delegreturn", "getacl", "setacl", "fs_locations", 37 | "rel_lkowner", "secinfo") 38 | for line in self.splitlines(): 39 | 40 | fields = line.split() 41 | if fields[0] == "proc4": # just grab NFSv4 stats 42 | assert int(fields[1]) == len(fields[2:]), ("reported field count (%d) does not match actual field count (%d)" % (int(fields[1]), len(fields[2:]))) 43 | for var in self.vars: 44 | self.set2[var] = fields[nfs4_names.index(var)] 45 | 46 | for name in self.vars: 47 | self.val[name] = (int(self.set2[name]) - int(self.set1[name])) * 1.0 / elapsed 48 | 49 | if step == op.delay: 50 | self.set1.update(self.set2) 51 | 52 | # vim:ts=4:sw=4:et 53 | -------------------------------------------------------------------------------- /plugins/dstat_ntp.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | global socket 4 | import socket 5 | 6 | global struct 7 | import struct 8 | 9 | ### FIXME: Implement millisecond granularity as well 10 | ### FIXME: Interrupts socket if data is overdue (more than 250ms ?) 11 | 12 | class dstat_plugin(dstat): 13 | """ 14 | Time from an NTP server. 15 | 16 | BEWARE: this dstat plugin typically takes a lot longer to run than 17 | system plugins and for that reason it is important to use an NTP server 18 | located nearby as well as make sure that it does not impact your other 19 | counters too much. 20 | """ 21 | 22 | def __init__(self): 23 | self.name = 'ntp' 24 | self.nick = ('date/time',) 25 | self.vars = ('time',) 26 | self.timefmt = os.getenv('DSTAT_TIMEFMT') or '%d-%m %H:%M:%S' 27 | self.ntpserver = os.getenv('DSTAT_NTPSERVER') or '0.fedora.pool.ntp.org' 28 | self.type = 's' 29 | self.width = len(time.strftime(self.timefmt, time.localtime())) 30 | self.scale = 0 31 | self.epoch = 2208988800 32 | # socket.setdefaulttimeout(0.25) 33 | self.socket = socket.socket( socket.AF_INET, socket.SOCK_DGRAM ) 34 | self.socket.settimeout(0.25) 35 | 36 | def gettime(self): 37 | self.socket.sendto( '\x1b' + 47 * '\0', ( self.ntpserver, 123 )) 38 | data, address = self.socket.recvfrom(1024) 39 | return struct.unpack( '!12I', data )[10] - self.epoch 40 | 41 | def check(self): 42 | try: 43 | self.gettime() 44 | except socket.gaierror: 45 | raise Exception('Failed to connect to NTP server %s.' % self.ntpserver) 46 | except socket.error: 47 | raise Exception('Error connecting to NTP server %s.' % self.ntpserver) 48 | 49 | def extract(self): 50 | try: 51 | self.val['time'] = time.strftime(self.timefmt, time.localtime(self.gettime())) 52 | except: 53 | self.val['time'] = theme['error'] + '-'.rjust(self.width-1) + ' ' 54 | 55 | def showcsv(self): 56 | return time.strftime(self.timefmt, time.localtime(self.gettime())) 57 | 58 | # vim:ts=4:sw=4:et 59 | -------------------------------------------------------------------------------- /plugins/dstat_postfix.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | def __init__(self): 5 | self.name = 'postfix' 6 | self.nick = ('inco', 'actv', 'dfrd', 'bnce', 'defr') 7 | self.vars = ('incoming', 'active', 'deferred', 'bounce', 'defer') 8 | self.type = 'd' 9 | self.width = 4 10 | self.scale = 100 11 | 12 | def check(self): 13 | if not os.access('/var/spool/postfix/active', os.R_OK): 14 | raise Exception('Cannot access postfix queues') 15 | 16 | def extract(self): 17 | for item in self.vars: 18 | self.val[item] = len(glob.glob('/var/spool/postfix/'+item+'/*/*')) 19 | 20 | # vim:ts=4:sw=4:et 21 | -------------------------------------------------------------------------------- /plugins/dstat_power.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | """ 5 | Power usage information from ACPI. 6 | 7 | Displays the power usage in watt per hour of your system's battery using 8 | ACPI information. This information is only available when the battery is 9 | being used (or being charged). 10 | """ 11 | 12 | def __init__(self): 13 | self.name = 'power' 14 | self.nick = ( 'usage', ) 15 | self.vars = ( 'rate', ) 16 | self.type = 'f' 17 | self.width = 5 18 | self.scale = 1 19 | self.rate = 0 20 | self.batteries = [] 21 | for battery in os.listdir('/proc/acpi/battery/'): 22 | for line in dopen('/proc/acpi/battery/'+battery+'/state').readlines(): 23 | l = line.split() 24 | if len(l) < 2: continue 25 | self.batteries.append(battery) 26 | break 27 | 28 | def check(self): 29 | if not self.batteries: 30 | raise Exception('No battery information found, no power usage statistics') 31 | 32 | def extract(self): 33 | amperes_drawn = 0 34 | voltage = 0 35 | watts_drawn = 0 36 | for battery in self.batteries: 37 | for line in dopen('/proc/acpi/battery/'+battery+'/state').readlines(): 38 | l = line.split() 39 | if len(l) < 3: continue 40 | if l[0] == 'present:' and l[1] != 'yes': continue 41 | if l[0:2] == ['charging','state:'] and l[2] != 'discharging': 42 | voltage = 0 43 | break 44 | if l[0:2] == ['present','voltage:']: 45 | voltage = int(l[2]) / 1000.0 46 | elif l[0:2] == ['present','rate:'] and l[3] == 'mW': 47 | watts_drawn = int(l[2]) / 1000.0 48 | elif l[0:2] == ['present','rate:'] and l[3] == 'mA': 49 | amperes_drawn = int(l[2]) / 1000.0 50 | 51 | self.rate = self.rate + watts_drawn + voltage * amperes_drawn 52 | 53 | ### Return error if we found no information 54 | if self.rate == 0: 55 | self.rate = -1 56 | 57 | if op.update: 58 | self.val['rate'] = self.rate / elapsed 59 | else: 60 | self.val['rate'] = self.rate 61 | 62 | if step == op.delay: 63 | self.rate = 0 64 | 65 | # vim:ts=4:sw=4:et 66 | -------------------------------------------------------------------------------- /plugins/dstat_proc_count.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | """ 5 | Total Number of processes on this system. 6 | """ 7 | def __init__(self): 8 | self.name = 'procs' 9 | self.vars = ('total',) 10 | self.type = 'd' 11 | self.width = 4 12 | self.scale = 10 13 | 14 | def extract(self): 15 | self.val['total'] = len([pid for pid in proc_pidlist()]) 16 | -------------------------------------------------------------------------------- /plugins/dstat_qmail.py: -------------------------------------------------------------------------------- 1 | ### Author: Tom Van Looy 2 | 3 | class dstat_plugin(dstat): 4 | """ 5 | port of qmail_qstat to dstat 6 | """ 7 | def __init__(self): 8 | self.name = 'qmail' 9 | self.nick = ('in_queue', 'not_prep') 10 | self.vars = ('mess', 'todo') 11 | self.type = 'd' 12 | self.width = 4 13 | self.scale = 100 14 | 15 | def check(self): 16 | for item in self.vars: 17 | if not os.access('/var/qmail/queue/'+item, os.R_OK): 18 | raise Exception('Cannot access qmail queues') 19 | 20 | def extract(self): 21 | for item in self.vars: 22 | self.val[item] = len(glob.glob('/var/qmail/queue/'+item+'/*/*')) 23 | 24 | # vim:ts=4:sw=4:et 25 | -------------------------------------------------------------------------------- /plugins/dstat_redis.py: -------------------------------------------------------------------------------- 1 | ### Author: Jihyun Yu 2 | 3 | global redis_host 4 | redis_host = os.getenv('DSTAT_REDIS_HOST') or "127.0.0.1" 5 | 6 | global redis_port 7 | redis_port = os.getenv('DSTAT_REDIS_PORT') or "6379" 8 | 9 | class dstat_plugin(dstat): 10 | def __init__(self): 11 | self.type = 'd' 12 | self.width = 7 13 | self.scale = 10000 14 | self.name = 'redis' 15 | self.nick = ('tps',) 16 | self.vars = ('tps',) 17 | self.cmdInfo = '*1\r\n$4\r\ninfo\r\n' 18 | 19 | def get_info(self): 20 | global socket 21 | import socket 22 | 23 | global redis_host 24 | global redis_port 25 | 26 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 27 | try: 28 | s.settimeout(0.1) 29 | s.connect((redis_host, int(redis_port))) 30 | s.send(self.cmdInfo) 31 | dict = {}; 32 | for line in s.recv(1024*1024).split('\r\n'): 33 | if line == "" or line[0] == '#' or line[0] == '*' or line[0] == '$': 34 | continue 35 | pair = line.split(':', 2) 36 | dict[pair[0]] = pair[1] 37 | return dict 38 | except: 39 | return {} 40 | finally: 41 | try: 42 | s.close() 43 | except: 44 | pass 45 | 46 | def extract(self): 47 | key = "instantaneous_ops_per_sec" 48 | dic = self.get_info() 49 | if key in dic: 50 | self.val['tps'] = int(dic[key]) 51 | 52 | # vim:ts=4:sw=4:et 53 | -------------------------------------------------------------------------------- /plugins/dstat_rpc.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | def __init__(self): 5 | self.name = 'rpc client' 6 | self.nick = ('call', 'retr', 'refr') 7 | self.vars = ('calls', 'retransmits', 'autorefreshes') 8 | self.type = 'd' 9 | self.width = 5 10 | self.scale = 1000 11 | self.open('/proc/net/rpc/nfs') 12 | 13 | def extract(self): 14 | for l in self.splitlines(): 15 | if not l or l[0] != 'rpc': continue 16 | for i, name in enumerate(self.vars): 17 | self.set2[name] = int(l[i+1]) 18 | 19 | for name in self.vars: 20 | self.val[name] = (self.set2[name] - self.set1[name]) * 1.0 / elapsed 21 | 22 | if step == op.delay: 23 | self.set1.update(self.set2) 24 | 25 | # vim:ts=4:sw=4:et 26 | -------------------------------------------------------------------------------- /plugins/dstat_rpcd.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | def __init__(self): 5 | self.name = 'rpc server' 6 | self.nick = ('call', 'erca', 'erau', 'ercl', 'xdrc') 7 | self.vars = ('calls', 'badcalls', 'badauth', 'badclnt', 'xdrcall') 8 | self.type = 'd' 9 | self.width = 5 10 | self.scale = 1000 11 | self.open('/proc/net/rpc/nfsd') 12 | 13 | def extract(self): 14 | for l in self.splitlines(): 15 | if not l or l[0] != 'rpc': continue 16 | for i, name in enumerate(self.vars): 17 | self.set2[name] = int(l[i+1]) 18 | 19 | for name in self.vars: 20 | self.val[name] = (self.set2[name] - self.set1[name]) * 1.0 / elapsed 21 | 22 | if step == op.delay: 23 | self.set1.update(self.set2) 24 | 25 | # vim:ts=4:sw=4:et 26 | -------------------------------------------------------------------------------- /plugins/dstat_sendmail.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | ### FIXME: Should read /var/log/mail/statistics or /etc/mail/statistics (format ?) 4 | 5 | class dstat_plugin(dstat): 6 | def __init__(self): 7 | self.name = 'sendmail' 8 | self.vars = ('queue',) 9 | self.type = 'd' 10 | self.width = 4 11 | self.scale = 100 12 | 13 | def check(self): 14 | if not os.access('/var/spool/mqueue', os.R_OK): 15 | raise Exception('Cannot access sendmail queue') 16 | 17 | def extract(self): 18 | self.val['queue'] = len(glob.glob('/var/spool/mqueue/qf*')) 19 | 20 | # vim:ts=4:sw=4:et 21 | -------------------------------------------------------------------------------- /plugins/dstat_snmp_cpu.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | def __init__(self): 5 | self.name = 'total cpu' 6 | self.vars = ( 'usr', 'sys', 'idl' ) 7 | self.type = 'p' 8 | self.width = 3 9 | self.scale = 34 10 | self.server = os.getenv('DSTAT_SNMPSERVER') or '192.168.1.1' 11 | self.community = os.getenv('DSTAT_SNMPCOMMUNITY') or 'public' 12 | 13 | def check(self): 14 | try: 15 | global cmdgen 16 | from pysnmp.entity.rfc3413.oneliner import cmdgen 17 | except: 18 | raise Exception('Needs pysnmp and pyasn1 modules') 19 | 20 | def extract(self): 21 | self.set2['usr'] = int(snmpget(self.server, self.community, (1,3,6,1,4,1,2021,11,50,0))) 22 | self.set2['sys'] = int(snmpget(self.server, self.community, (1,3,6,1,4,1,2021,11,52,0))) 23 | self.set2['idl'] = int(snmpget(self.server, self.community, (1,3,6,1,4,1,2021,11,53,0))) 24 | # self.set2['usr'] = int(snmpget(self.server, self.community, (('UCD-SNMP-MIB', 'ssCpuRawUser'), 0))) 25 | # self.set2['sys'] = int(snmpget(self.server, self.community, (('UCD-SNMP-MIB', 'ssCpuRawSystem'), 0))) 26 | # self.set2['idl'] = int(snmpget(self.server, self.community, (('UCD-SNMP-MIB', 'ssCpuRawIdle'), 0))) 27 | 28 | if update: 29 | for name in self.vars: 30 | if sum(self.set2.values()) > sum(self.set1.values()): 31 | self.val[name] = 100.0 * (self.set2[name] - self.set1[name]) / (sum(self.set2.values()) - sum(self.set1.values())) 32 | else: 33 | self.val[name] = 0 34 | 35 | if step == op.delay: 36 | self.set1.update(self.set2) 37 | 38 | # vim:ts=4:sw=4:et 39 | -------------------------------------------------------------------------------- /plugins/dstat_snmp_load.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | def __init__(self): 5 | self.name = 'load avg' 6 | self.nick = ('1m', '5m', '15m') 7 | self.vars = ('load1', 'load5', 'load15') 8 | self.type = 'f' 9 | self.width = 4 10 | self.scale = 0.5 11 | self.server = os.getenv('DSTAT_SNMPSERVER') or '192.168.1.1' 12 | self.community = os.getenv('DSTAT_SNMPCOMMUNITY') or 'public' 13 | 14 | def check(self): 15 | try: 16 | global cmdgen 17 | from pysnmp.entity.rfc3413.oneliner import cmdgen 18 | except: 19 | raise Exception('Needs pysnmp and pyasn1 modules') 20 | 21 | def extract(self): 22 | list(map(lambda x, y: self.val.update({x: float(y)}), self.vars, snmpwalk(self.server, self.community, (1,3,6,1,4,1,2021,10,1,3)))) 23 | 24 | # vim:ts=4:sw=4:et 25 | -------------------------------------------------------------------------------- /plugins/dstat_snmp_mem.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | def __init__(self): 5 | self.name = 'memory usage' 6 | self.nick = ('used', 'buff', 'cach', 'free') 7 | self.vars = ('MemUsed', 'Buffers', 'Cached', 'MemFree') 8 | self.server = os.getenv('DSTAT_SNMPSERVER') or '192.168.1.1' 9 | self.community = os.getenv('DSTAT_SNMPCOMMUNITY') or 'public' 10 | 11 | def check(self): 12 | try: 13 | global cmdgen 14 | from pysnmp.entity.rfc3413.oneliner import cmdgen 15 | except: 16 | raise Exception('Needs pysnmp and pyasn1 modules') 17 | 18 | def extract(self): 19 | self.val['MemTotal'] = int(snmpget(self.server, self.community, (1,3,6,1,4,1,2021,4,5,0))) * 1024 20 | self.val['MemFree'] = int(snmpget(self.server, self.community, (1,3,6,1,4,1,2021,4,11,0))) * 1024 21 | # self.val['Shared'] = int(snmpget(self.server, self.community, (1,3,6,1,4,1,2021,4,13,0))) * 1024 22 | self.val['Buffers'] = int(snmpget(self.server, self.community, (1,3,6,1,4,1,2021,4,14,0))) * 1024 23 | self.val['Cached'] = int(snmpget(self.server, self.community, (1,3,6,1,4,1,2021,4,15,0))) * 1024 24 | 25 | self.val['MemUsed'] = self.val['MemTotal'] - self.val['MemFree'] 26 | 27 | # vim:ts=4:sw=4:et 28 | -------------------------------------------------------------------------------- /plugins/dstat_snmp_net.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | def __init__(self): 5 | self.nick = ('recv', 'send') 6 | self.type = 'b' 7 | self.cols = 2 8 | self.server = os.getenv('DSTAT_SNMPSERVER') or '192.168.1.1' 9 | self.community = os.getenv('DSTAT_SNMPCOMMUNITY') or 'public' 10 | 11 | def check(self): 12 | try: 13 | global cmdgen 14 | from pysnmp.entity.rfc3413.oneliner import cmdgen 15 | except: 16 | raise Exception('Needs pysnmp and pyasn1 modules') 17 | 18 | def name(self): 19 | return self.vars 20 | 21 | def vars(self): 22 | return [ str(x) for x in snmpwalk(self.server, self.community, (1,3,6,1,2,1,2,2,1,2)) ] 23 | 24 | def extract(self): 25 | list(map(lambda x, y, z: self.set2.update({x: (int(y), int(z))}), self.vars, snmpwalk(self.server, self.community, (1,3,6,1,2,1,2,2,1,10)), snmpwalk(self.server, self.community, (1,3,6,1,2,1,2,2,1,16)))) 26 | 27 | if update: 28 | for name in self.set2: 29 | self.val[name] = list(map(lambda x, y: (y - x) * 1.0 / elapsed, self.set1[name], self.set2[name])) 30 | 31 | if step == op.delay: 32 | self.set1.update(self.set2) 33 | 34 | # vim:ts=4:sw=4:et 35 | -------------------------------------------------------------------------------- /plugins/dstat_snmp_net_err.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | def __init__(self): 5 | self.nick = ('error', ) 6 | self.type = 'b' 7 | self.cols = 1 8 | self.server = os.getenv('DSTAT_SNMPSERVER') or '192.168.1.1' 9 | self.community = os.getenv('DSTAT_SNMPCOMMUNITY') or 'public' 10 | 11 | def check(self): 12 | try: 13 | global cmdgen 14 | from pysnmp.entity.rfc3413.oneliner import cmdgen 15 | except: 16 | raise Exception('Needs pysnmp and pyasn1 modules') 17 | 18 | def name(self): 19 | return self.vars 20 | 21 | def vars(self): 22 | return [ str(x) for x in snmpwalk(self.server, self.community, (1,3,6,1,2,1,2,2,1,2)) ] 23 | 24 | def extract(self): 25 | list(map(lambda x, y: self.set2.update({x: (int(y), )}), self.vars, snmpwalk(self.server, self.community, (1,3,6,1,2,1,2,2,1,20)))) 26 | 27 | if update: 28 | for name in self.set2: 29 | # self.val[name] = list(map(lambda x, y: (y - x) * 1.0 / elapsed, self.set1[name], self.set2[name])) 30 | self.val[name] = list(map(lambda x, y: (y - x) * 1.0, self.set1[name], self.set2[name])) 31 | 32 | if step == op.delay: 33 | self.set1.update(self.set2) 34 | 35 | # vim:ts=4:sw=4:et 36 | -------------------------------------------------------------------------------- /plugins/dstat_snmp_sys.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | def __init__(self): 5 | self.name = 'system' 6 | self.nick = ('int', 'csw') 7 | self.vars = ('intr', 'ctxt') 8 | self.type = 'd' 9 | self.width = 5 10 | self.scale = 1000 11 | self.server = os.getenv('DSTAT_SNMPSERVER') or '192.168.1.1' 12 | self.community = os.getenv('DSTAT_SNMPCOMMUNITY') or 'public' 13 | 14 | def check(self): 15 | try: 16 | global cmdgen 17 | from pysnmp.entity.rfc3413.oneliner import cmdgen 18 | except: 19 | raise Exception('Needs pysnmp and pyasn1 modules') 20 | 21 | def extract(self): 22 | self.set2['intr'] = int(snmpget(self.server, self.community, (1,3,6,1,4,1,2021,11,59,0))) 23 | self.set2['ctxt'] = int(snmpget(self.server, self.community, (1,3,6,1,4,1,2021,11,60,0))) 24 | 25 | if update: 26 | for name in self.vars: 27 | self.val[name] = (self.set2[name] - self.set1[name]) * 1.0 / elapsed 28 | 29 | if step == op.delay: 30 | self.set1.update(self.set2) 31 | 32 | # vim:ts=4:sw=4:et 33 | -------------------------------------------------------------------------------- /plugins/dstat_snooze.py: -------------------------------------------------------------------------------- 1 | class dstat_plugin(dstat): 2 | def __init__(self): 3 | self.name = 'snooze' 4 | self.vars = ('snooze',) 5 | self.type = 's' 6 | self.width = 6 7 | self.scale = 0 8 | self.before = time.time() 9 | 10 | def extract(self): 11 | now = time.time() 12 | if loop != 0: 13 | self.val['snooze'] = now - self.before 14 | else: 15 | self.val['snooze'] = self.before 16 | if step == op.delay: 17 | self.before = now 18 | 19 | def show(self): 20 | if self.val['snooze'] > step + 1: 21 | return ansi['default'] + ' -' 22 | 23 | if op.blackonwhite: 24 | textcolor = 'black' 25 | if step != op.delay: 26 | textcolor = 'darkgray' 27 | else: 28 | textcolor = 'white' 29 | if step != op.delay: 30 | textcolor = 'gray' 31 | 32 | snoze, c = fchg(self.val['snooze'], 6, 1000) 33 | 34 | return color[textcolor] + snoze 35 | -------------------------------------------------------------------------------- /plugins/dstat_squid.py: -------------------------------------------------------------------------------- 1 | ### Authority: Jason Friedland 2 | 3 | # This plugin has been tested with: 4 | # - Dstat 0.6.7 5 | # - CentOS release 5.4 (Final) 6 | # - Python 2.4.3 7 | # - Squid 2.6 and 2.7 8 | 9 | global squidclient_options 10 | squidclient_options = os.getenv('DSTAT_SQUID_OPTS') # -p 8080 11 | 12 | class dstat_plugin(dstat): 13 | ''' 14 | Provides various Squid statistics. 15 | ''' 16 | def __init__(self): 17 | self.name = 'squid status' 18 | self.type = 's' 19 | self.width = 5 20 | self.scale = 1000 21 | self.vars = ('Number of file desc currently in use', 22 | 'CPU Usage, 5 minute avg', 23 | 'Total accounted', 24 | 'Number of clients accessing cache', 25 | 'Mean Object Size') 26 | self.nick = ('fdesc', 27 | 'cpu5', 28 | 'mem', 29 | 'clnts', 30 | 'objsz') 31 | 32 | def check(self): 33 | if not os.access('/usr/sbin/squidclient', os.X_OK): 34 | raise Exception('Needs squidclient binary') 35 | cmd_test('/usr/sbin/squidclient %s mgr:info' % squidclient_options) 36 | return True 37 | 38 | def extract(self): 39 | try: 40 | for l in cmd_splitlines('/usr/sbin/squidclient %s mgr:info' % squidclient_options, ':'): 41 | if l[0].strip() in self.vars: 42 | self.val[l[0].strip()] = l[1].strip() 43 | break 44 | except IOError as e: 45 | if op.debug > 1: print('%s: lost pipe to squidclient, %s' % (self.filename, e)) 46 | for name in self.vars: self.val[name] = -1 47 | except Exception as e: 48 | if op.debug > 1: print('%s: exception' (self.filename, e)) 49 | for name in self.vars: self.val[name] = -1 50 | 51 | # vim:ts=4:sw=4:et 52 | -------------------------------------------------------------------------------- /plugins/dstat_test.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | ''' 5 | Provides a test playground to test syntax and structure. 6 | ''' 7 | def __init__(self): 8 | self.name = 'test' 9 | self.nick = ( 'f1', 'f2' ) 10 | self.vars = ( 'f1', 'f2' ) 11 | # self.type = 'd' 12 | # self.width = 4 13 | # self.scale = 20 14 | self.type = 's' 15 | self.width = 4 16 | self.scale = 0 17 | 18 | def extract(self): 19 | # Self.val = { 'f1': -1, 'f2': -1 } 20 | self.val = { 'f1': 'test', 'f2': 'test' } 21 | 22 | # vim:ts=4:sw=4:et 23 | -------------------------------------------------------------------------------- /plugins/dstat_thermal.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | def __init__(self): 5 | self.name = 'thermal' 6 | self.type = 'd' 7 | self.width = 3 8 | self.scale = 20 9 | 10 | if os.path.exists('/sys/devices/virtual/thermal/'): 11 | self.nick = [] 12 | self.vars = [] 13 | for zone in os.listdir('/sys/devices/virtual/thermal/'): 14 | zone_split=zone.split("thermal_zone") 15 | if len(zone_split) == 2: 16 | self.vars.append(zone) 17 | name="".join(["tz",zone_split[1]]) 18 | self.nick.append(name) 19 | 20 | elif os.path.exists('/sys/bus/acpi/devices/LNXTHERM:01/thermal_zone/'): 21 | self.vars = os.listdir('/sys/bus/acpi/devices/LNXTHERM:01/thermal_zone/') 22 | self.nick = [] 23 | for name in self.vars: 24 | self.nick.append(name.lower()) 25 | 26 | elif os.path.exists('/proc/acpi/ibm/thermal'): 27 | self.namelist = ['cpu', 'pci', 'hdd', 'cpu', 'ba0', 'unk', 'ba1', 'unk'] 28 | self.nick = [] 29 | for line in dopen('/proc/acpi/ibm/thermal'): 30 | l = line.split() 31 | for i, name in enumerate(self.namelist): 32 | if int(l[i+1]) > 0: 33 | self.nick.append(name) 34 | self.vars = self.nick 35 | 36 | elif os.path.exists('/proc/acpi/thermal_zone/'): 37 | self.vars = os.listdir('/proc/acpi/thermal_zone/') 38 | # self.nick = [name.lower() for name in self.vars] 39 | self.nick = [] 40 | for name in self.vars: 41 | self.nick.append(name.lower()) 42 | 43 | else: 44 | raise Exception('Needs kernel thermal, ACPI or IBM-ACPI support') 45 | 46 | def check(self): 47 | if not os.path.exists('/proc/acpi/ibm/thermal') and \ 48 | not os.path.exists('/proc/acpi/thermal_zone/') and \ 49 | not os.path.exists('/sys/devices/virtual/thermal/') and \ 50 | not os.path.exists('/sys/bus/acpi/devices/LNXTHERM:00/thermal_zone/'): 51 | raise Exception('Needs kernel thermal, ACPI or IBM-ACPI support') 52 | 53 | def extract(self): 54 | if os.path.exists('/sys/devices/virtual/thermal/'): 55 | for zone in self.vars: 56 | for line in dopen('/sys/devices/virtual/thermal/'+zone+'/temp').readlines(): 57 | l = line.split() 58 | self.val[zone] = int(l[0]) 59 | elif os.path.exists('/sys/bus/acpi/devices/LNXTHERM:01/thermal_zone/'): 60 | for zone in self.vars: 61 | if os.path.isdir('/sys/bus/acpi/devices/LNXTHERM:01/thermal_zone/'+zone) == False: 62 | for line in dopen('/sys/bus/acpi/devices/LNXTHERM:01/thermal_zone/'+zone).readlines(): 63 | l = line.split() 64 | if l[0].isdigit() == True: 65 | self.val[zone] = int(l[0]) 66 | else: 67 | self.val[zone] = 0 68 | elif os.path.exists('/proc/acpi/ibm/thermal'): 69 | for line in dopen('/proc/acpi/ibm/thermal'): 70 | l = line.split() 71 | for i, name in enumerate(self.namelist): 72 | if int(l[i+1]) > 0: 73 | self.val[name] = int(l[i+1]) 74 | elif os.path.exists('/proc/acpi/thermal_zone/'): 75 | for zone in self.vars: 76 | for line in dopen('/proc/acpi/thermal_zone/'+zone+'/temperature').readlines(): 77 | l = line.split() 78 | self.val[zone] = int(l[1]) 79 | 80 | # vim:ts=4:sw=4:et 81 | -------------------------------------------------------------------------------- /plugins/dstat_top_bio.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | """ 5 | Top most expensive block I/O process. 6 | 7 | Displays the name of the most expensive block I/O process. 8 | """ 9 | def __init__(self): 10 | self.name = 'most expensive' 11 | self.vars = ('block i/o process',) 12 | self.type = 's' 13 | self.width = 22 14 | self.scale = 0 15 | self.pidset1 = {} 16 | 17 | def check(self): 18 | if not os.access('/proc/self/io', os.R_OK): 19 | raise Exception('Kernel has no per-process I/O accounting [CONFIG_TASK_IO_ACCOUNTING], use at least 2.6.20') 20 | 21 | def extract(self): 22 | self.output = '' 23 | self.pidset2 = {} 24 | self.val['usage'] = 0.0 25 | for pid in proc_pidlist(): 26 | try: 27 | ### Reset values 28 | if pid not in self.pidset2: 29 | self.pidset2[pid] = {'read_bytes:': 0, 'write_bytes:': 0} 30 | if pid not in self.pidset1: 31 | self.pidset1[pid] = {'read_bytes:': 0, 'write_bytes:': 0} 32 | 33 | ### Extract name 34 | name = proc_splitline('/proc/%s/stat' % pid)[1][1:-1] 35 | 36 | ### Extract counters 37 | for l in proc_splitlines('/proc/%s/io' % pid): 38 | if len(l) != 2: continue 39 | self.pidset2[pid][l[0]] = int(l[1]) 40 | except IOError: 41 | continue 42 | except IndexError: 43 | continue 44 | 45 | read_usage = (self.pidset2[pid]['read_bytes:'] - self.pidset1[pid]['read_bytes:']) * 1.0 / elapsed 46 | write_usage = (self.pidset2[pid]['write_bytes:'] - self.pidset1[pid]['write_bytes:']) * 1.0 / elapsed 47 | usage = read_usage + write_usage 48 | 49 | ### Get the process that spends the most jiffies 50 | if usage > self.val['usage']: 51 | self.val['usage'] = usage 52 | self.val['read_usage'] = read_usage 53 | self.val['write_usage'] = write_usage 54 | self.val['pid'] = pid 55 | self.val['name'] = getnamebypid(pid, name) 56 | # st = os.stat("/proc/%s" % pid) 57 | 58 | if step == op.delay: 59 | self.pidset1 = self.pidset2 60 | 61 | if self.val['usage'] != 0.0: 62 | self.output = '%-*s%s %s' % (self.width-11, self.val['name'][0:self.width-11], cprint(self.val['read_usage'], 'd', 5, 1024), cprint(self.val['write_usage'], 'd', 5, 1024)) 63 | 64 | ### Debug (show PID) 65 | # self.output = '%*s %-*s%s %s' % (5, self.val['pid'], self.width-17, self.val['name'][0:self.width-17], cprint(self.val['read_usage'], 'd', 5, 1024), cprint(self.val['write_usage'], 'd', 5, 1024)) 66 | 67 | def showcsv(self): 68 | return '%s / %d:%d' % (self.val['name'], self.val['read_usage'], self.val['write_usage']) 69 | 70 | # vim:ts=4:sw=4:et 71 | -------------------------------------------------------------------------------- /plugins/dstat_top_bio_adv.py: -------------------------------------------------------------------------------- 1 | ### Dstat all I/O process plugin 2 | ### Displays all processes' I/O read/write stats and CPU usage 3 | ### 4 | ### Authority: Guillermo Cantu Luna 5 | 6 | class dstat_plugin(dstat): 7 | def __init__(self): 8 | self.name = 'most expensive block i/o process' 9 | self.vars = ('process pid read write cpu',) 10 | self.type = 's' 11 | self.width = 40 12 | self.scale = 0 13 | self.pidset1 = {} 14 | 15 | def check(self): 16 | if not os.access('/proc/self/io', os.R_OK): 17 | raise Exception('Kernel has no per-process I/O accounting [CONFIG_TASK_IO_ACCOUNTING], use at least 2.6.20') 18 | return True 19 | 20 | def extract(self): 21 | self.output = '' 22 | self.pidset2 = {} 23 | self.val['usage'] = 0.0 24 | for pid in proc_pidlist(): 25 | try: 26 | ### Reset values 27 | if pid not in self.pidset2: 28 | self.pidset2[pid] = {'read_bytes:': 0, 'write_bytes:': 0, 'cputime:': 0, 'cpuper:': 0} 29 | if pid not in self.pidset1: 30 | self.pidset1[pid] = {'read_bytes:': 0, 'write_bytes:': 0, 'cputime:': 0, 'cpuper:': 0} 31 | 32 | ### Extract name 33 | name = proc_splitline('/proc/%s/stat' % pid)[1][1:-1] 34 | 35 | ### Extract counters 36 | for l in proc_splitlines('/proc/%s/io' % pid): 37 | if len(l) != 2: continue 38 | self.pidset2[pid][l[0]] = int(l[1]) 39 | 40 | ### Get CPU usage 41 | l = proc_splitline('/proc/%s/stat' % pid) 42 | if len(l) < 15: 43 | cpu_usage = 0 44 | else: 45 | self.pidset2[pid]['cputime:'] = int(l[13]) + int(l[14]) 46 | cpu_usage = (self.pidset2[pid]['cputime:'] - self.pidset1[pid]['cputime:']) * 1.0 / elapsed / cpunr 47 | 48 | except ValueError: 49 | continue 50 | except IOError: 51 | continue 52 | except IndexError: 53 | continue 54 | 55 | read_usage = (self.pidset2[pid]['read_bytes:'] - self.pidset1[pid]['read_bytes:']) * 1.0 / elapsed 56 | write_usage = (self.pidset2[pid]['write_bytes:'] - self.pidset1[pid]['write_bytes:']) * 1.0 / elapsed 57 | usage = read_usage + write_usage 58 | 59 | ### Get the process that spends the most jiffies 60 | if usage > self.val['usage']: 61 | self.val['usage'] = usage 62 | self.val['read_usage'] = read_usage 63 | self.val['write_usage'] = write_usage 64 | self.val['pid'] = pid 65 | self.val['name'] = getnamebypid(pid, name) 66 | self.val['cpu_usage'] = cpu_usage 67 | 68 | if step == op.delay: 69 | self.pidset1 = self.pidset2 70 | 71 | if self.val['usage'] != 0.0: 72 | self.output = '%-*s%s%-5s%s%s%s%s%%' % (self.width-14-len(pid), self.val['name'][0:self.width-14-len(pid)], color['darkblue'], self.val['pid'], cprint(self.val['read_usage'], 'd', 5, 1024), cprint(self.val['write_usage'], 'd', 5, 1024), cprint(self.val['cpu_usage'], 'f', 3, 34), color['darkgray']) 73 | 74 | def showcsv(self): 75 | return 'Top: %s\t%s\t%s\t%s' % (self.val['name'][0:self.width-20], self.val['read_usage'], self.val['write_usage'], self.val['cpu_usage']) 76 | -------------------------------------------------------------------------------- /plugins/dstat_top_childwait.py: -------------------------------------------------------------------------------- 1 | ### Dstat most expensive process plugin 2 | ### Displays the name of the most expensive process 3 | ### 4 | ### Authority: dag@wieers.com 5 | 6 | global cpunr 7 | 8 | class dstat_plugin(dstat): 9 | def __init__(self): 10 | self.name = 'most waiting for' 11 | self.vars = ('child process',) 12 | self.type = 's' 13 | self.width = 16 14 | self.scale = 0 15 | 16 | def extract(self): 17 | self.set2 = {} 18 | self.val['max'] = 0.0 19 | for pid in proc_pidlist(): 20 | try: 21 | ### Using dopen() will cause too many open files 22 | l = proc_splitline('/proc/%s/stat' % pid) 23 | except IOError: 24 | continue 25 | 26 | if len(l) < 15: continue 27 | 28 | ### Reset previous value if it doesn't exist 29 | if pid not in self.set1: 30 | self.set1[pid] = 0 31 | 32 | self.set2[pid] = int(l[15]) + int(l[16]) 33 | usage = (self.set2[pid] - self.set1[pid]) * 1.0 / elapsed / cpunr 34 | 35 | ### Is it a new topper ? 36 | if usage <= self.val['max']: continue 37 | 38 | self.val['max'] = usage 39 | self.val['name'] = getnamebypid(pid, l[1][1:-1]) 40 | self.val['pid'] = pid 41 | 42 | ### Debug (show PID) 43 | # self.val['process'] = '%*s %-*s' % (5, self.val['pid'], self.width-6, self.val['name']) 44 | 45 | if step == op.delay: 46 | self.set1 = self.set2 47 | 48 | def show(self): 49 | if self.val['max'] == 0.0: 50 | return '%-*s' % (self.width, '') 51 | else: 52 | return '%s%-*s%s' % (theme['default'], self.width-3, self.val['name'][0:self.width-3], cprint(self.val['max'], 'p', 3, 34)) 53 | 54 | def showcsv(self): 55 | return '%s / %d%%' % (self.val['name'], self.val['max']) 56 | 57 | # vim:ts=4:sw=4:et 58 | -------------------------------------------------------------------------------- /plugins/dstat_top_cpu.py: -------------------------------------------------------------------------------- 1 | ### Authority: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | """ 5 | Most expensive CPU process. 6 | 7 | Displays the process that uses the CPU the most during the monitored 8 | interval. The value displayed is the percentage of CPU time for the total 9 | amount of CPU processing power. Based on per process CPU information. 10 | """ 11 | def __init__(self): 12 | self.name = 'most expensive' 13 | self.vars = ('cpu process',) 14 | self.type = 's' 15 | self.width = 16 16 | self.scale = 0 17 | self.pidset1 = {} 18 | 19 | def extract(self): 20 | self.output = '' 21 | self.pidset2 = {} 22 | self.val['max'] = 0.0 23 | for pid in proc_pidlist(): 24 | try: 25 | ### Using dopen() will cause too many open files 26 | l = proc_splitline('/proc/%s/stat' % pid) 27 | except IOError: 28 | continue 29 | 30 | if len(l) < 15: continue 31 | 32 | ### Reset previous value if it doesn't exist 33 | if pid not in self.pidset1: 34 | self.pidset1[pid] = 0 35 | 36 | self.pidset2[pid] = int(l[13]) + int(l[14]) 37 | usage = (self.pidset2[pid] - self.pidset1[pid]) * 1.0 / elapsed / cpunr 38 | 39 | ### Is it a new topper ? 40 | if usage < self.val['max']: continue 41 | 42 | name = l[1][1:-1] 43 | 44 | self.val['max'] = usage 45 | self.val['pid'] = pid 46 | self.val['name'] = getnamebypid(pid, name) 47 | # self.val['name'] = name 48 | 49 | if self.val['max'] != 0.0: 50 | self.output = '%-*s%s' % (self.width-3, self.val['name'][0:self.width-3], cprint(self.val['max'], 'f', 3, 34)) 51 | 52 | ### Debug (show PID) 53 | # self.output = '%*s %-*s' % (5, self.val['pid'], self.width-6, self.val['name']) 54 | 55 | if step == op.delay: 56 | self.pidset1 = self.pidset2 57 | 58 | def showcsv(self): 59 | return '%s / %d%%' % (self.val['name'], self.val['max']) 60 | 61 | # vim:ts=4:sw=4:et 62 | -------------------------------------------------------------------------------- /plugins/dstat_top_cpu_adv.py: -------------------------------------------------------------------------------- 1 | ### Dstat all I/O process plugin 2 | ### Displays all processes' I/O read/write stats and CPU usage 3 | ### 4 | ### Authority: Guillermo Cantu Luna 5 | 6 | class dstat_plugin(dstat): 7 | def __init__(self): 8 | self.name = 'most expensive cpu process' 9 | self.vars = ('process pid cpu read write',) 10 | self.type = 's' 11 | self.width = 40 12 | self.scale = 0 13 | self.pidset1 = {} 14 | 15 | def check(self): 16 | if not os.access('/proc/self/io', os.R_OK): 17 | raise Exception('Kernel has no per-process I/O accounting [CONFIG_TASK_IO_ACCOUNTING], use at least 2.6.20') 18 | return True 19 | 20 | def extract(self): 21 | self.output = '' 22 | self.pidset2 = {} 23 | self.val['cpu_usage'] = 0 24 | for pid in proc_pidlist(): 25 | try: 26 | ### Reset values 27 | if pid not in self.pidset2: 28 | self.pidset2[pid] = {'rchar:': 0, 'wchar:': 0, 'cputime:': 0, 'cpuper:': 0} 29 | if pid not in self.pidset1: 30 | self.pidset1[pid] = {'rchar:': 0, 'wchar:': 0, 'cputime:': 0, 'cpuper:': 0} 31 | 32 | ### Extract name 33 | name = proc_splitline('/proc/%s/stat' % pid)[1][1:-1] 34 | 35 | ### Extract counters 36 | for l in proc_splitlines('/proc/%s/io' % pid): 37 | if len(l) != 2: continue 38 | self.pidset2[pid][l[0]] = int(l[1]) 39 | 40 | ### Get CPU usage 41 | l = proc_splitline('/proc/%s/stat' % pid) 42 | if len(l) < 15: 43 | cpu_usage = 0.0 44 | else: 45 | self.pidset2[pid]['cputime:'] = int(l[13]) + int(l[14]) 46 | cpu_usage = (self.pidset2[pid]['cputime:'] - self.pidset1[pid]['cputime:']) * 1.0 / elapsed / cpunr 47 | 48 | except ValueError: 49 | continue 50 | except IOError: 51 | continue 52 | except IndexError: 53 | continue 54 | 55 | read_usage = (self.pidset2[pid]['rchar:'] - self.pidset1[pid]['rchar:']) * 1.0 / elapsed 56 | write_usage = (self.pidset2[pid]['wchar:'] - self.pidset1[pid]['wchar:']) * 1.0 / elapsed 57 | 58 | ### Get the process that spends the most jiffies 59 | if cpu_usage > self.val['cpu_usage']: 60 | self.val['read_usage'] = read_usage 61 | self.val['write_usage'] = write_usage 62 | self.val['pid'] = pid 63 | self.val['name'] = getnamebypid(pid, name) 64 | self.val['cpu_usage'] = cpu_usage 65 | 66 | if step == op.delay: 67 | self.pidset1 = self.pidset2 68 | 69 | if self.val['cpu_usage'] != 0.0: 70 | self.output = '%-*s%s%-5s%s%s%%%s%s' % (self.width-14-len(pid), self.val['name'][0:self.width-14-len(pid)], color['darkblue'], self.val['pid'], cprint(self.val['cpu_usage'], 'f', 3, 34), color['darkgray'],cprint(self.val['read_usage'], 'd', 5, 1024), cprint(self.val['write_usage'], 'd', 5, 1024)) 71 | 72 | 73 | def showcsv(self): 74 | return 'Top: %s\t%s\t%s\t%s' % (self.val['name'][0:self.width-20], self.val['cpu_usage'], self.val['read_usage'], self.val['write_usage']) 75 | -------------------------------------------------------------------------------- /plugins/dstat_top_cputime.py: -------------------------------------------------------------------------------- 1 | ### Authority: dag@wieers.com 2 | 3 | ### For more information, see: 4 | ### http://eaglet.rain.com/rick/linux/schedstat/ 5 | 6 | class dstat_plugin(dstat): 7 | """ 8 | Name and total amount of CPU time consumed in milliseconds of the process 9 | that has the highest total amount of cputime for the measured timeframe. 10 | 11 | On a system with one CPU and one core, the total cputime is 1000ms. On a 12 | system with two cores the total cputime is 2000ms. 13 | """ 14 | 15 | def __init__(self): 16 | self.name = 'highest total' 17 | self.vars = ('cputime process',) 18 | self.type = 's' 19 | self.width = 17 20 | self.scale = 0 21 | self.pidset1 = {} 22 | 23 | def check(self): 24 | if not os.access('/proc/self/schedstat', os.R_OK): 25 | raise Exception('Kernel has no scheduler statistics [CONFIG_SCHEDSTATS], use at least 2.6.12') 26 | 27 | def extract(self): 28 | self.output = '' 29 | self.pidset2 = {} 30 | self.val['result'] = 0 31 | for pid in proc_pidlist(): 32 | try: 33 | ### Reset values 34 | if pid not in self.pidset1: 35 | self.pidset1[pid] = {'run_ticks': 0} 36 | 37 | ### Extract name 38 | name = proc_splitline('/proc/%s/stat' % pid)[1][1:-1] 39 | 40 | ### Extract counters 41 | l = proc_splitline('/proc/%s/schedstat' % pid) 42 | except IOError: 43 | continue 44 | except IndexError: 45 | continue 46 | 47 | if len(l) != 3: continue 48 | 49 | self.pidset2[pid] = {'run_ticks': int(l[0])} 50 | 51 | totrun = (self.pidset2[pid]['run_ticks'] - self.pidset1[pid]['run_ticks']) * 1.0 / elapsed 52 | 53 | ### Get the process that spends the most jiffies 54 | if totrun > self.val['result']: 55 | self.val['result'] = totrun 56 | self.val['pid'] = pid 57 | self.val['name'] = getnamebypid(pid, name) 58 | 59 | if step == op.delay: 60 | self.pidset1 = self.pidset2 61 | 62 | if self.val['result'] != 0.0: 63 | self.output = '%-*s%s' % (self.width-4, self.val['name'][0:self.width-4], cprint(self.val['result'], 'd', 4, 100)) 64 | 65 | ### Debug (show PID) 66 | # self.output = '%*s %-*s' % (5, self.val['pid'], self.width-6, self.val['name']) 67 | 68 | def showcsv(self): 69 | return '%s / %.4f' % (self.val['name'], self.val['result']) 70 | 71 | # vim:ts=4:sw=4:et 72 | -------------------------------------------------------------------------------- /plugins/dstat_top_cputime_avg.py: -------------------------------------------------------------------------------- 1 | ### Authority: dag@wieers.com 2 | 3 | ### For more information, see: 4 | ### http://eaglet.rain.com/rick/linux/schedstat/ 5 | 6 | class dstat_plugin(dstat): 7 | """ 8 | Name and average amount of CPU time consumed in milliseconds of the process 9 | that has the highest average amount of cputime for the different slices for 10 | the measured timeframe. 11 | 12 | On a system with one CPU and one core, the total cputime is 1000ms. On a 13 | system with two cores the total cputime is 2000ms. 14 | """ 15 | 16 | def __init__(self): 17 | self.name = 'highest average' 18 | self.vars = ('cputime process',) 19 | self.type = 's' 20 | self.width = 17 21 | self.scale = 0 22 | self.pidset1 = {} 23 | 24 | def check(self): 25 | if not os.access('/proc/self/schedstat', os.R_OK): 26 | raise Exception('Kernel has no scheduler statistics [CONFIG_SCHEDSTATS], use at least 2.6.12') 27 | 28 | def extract(self): 29 | self.output = '' 30 | self.pidset2 = {} 31 | self.val['result'] = 0 32 | for pid in proc_pidlist(): 33 | try: 34 | ### Reset values 35 | if pid not in self.pidset1: 36 | self.pidset1[pid] = {'run_ticks': 0, 'ran': 0} 37 | 38 | ### Extract name 39 | name = proc_splitline('/proc/%s/stat' % pid)[1][1:-1] 40 | 41 | ### Extract counters 42 | l = proc_splitline('/proc/%s/schedstat' % pid) 43 | except IOError: 44 | continue 45 | except IndexError: 46 | continue 47 | 48 | if len(l) != 3: continue 49 | 50 | self.pidset2[pid] = {'run_ticks': int(l[0]), 'ran': int(l[2])} 51 | 52 | if self.pidset2[pid]['ran'] - self.pidset1[pid]['ran'] > 0: 53 | avgrun = (self.pidset2[pid]['run_ticks'] - self.pidset1[pid]['run_ticks']) * 1.0 / (self.pidset2[pid]['ran'] - self.pidset1[pid]['ran']) / elapsed 54 | else: 55 | avgrun = 0 56 | 57 | ### Get the process that spends the most jiffies 58 | if avgrun > self.val['result']: 59 | self.val['result'] = avgrun 60 | self.val['pid'] = pid 61 | self.val['name'] = getnamebypid(pid, name) 62 | 63 | if step == op.delay: 64 | self.pidset1 = self.pidset2 65 | 66 | if self.val['result'] != 0.0: 67 | self.output = '%-*s%s' % (self.width-4, self.val['name'][0:self.width-4], cprint(self.val['result'], 'f', 4, 100)) 68 | 69 | ### Debug (show PID) 70 | # self.output = '%*s %-*s' % (5, self.val['pid'], self.width-6, self.val['name']) 71 | 72 | def showcsv(self): 73 | return '%s / %.4f' % (self.val['name'], self.val['result']) 74 | 75 | # vim:ts=4:sw=4:et 76 | -------------------------------------------------------------------------------- /plugins/dstat_top_int.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | """ 5 | Top interrupt 6 | 7 | Displays the name of the most frequent interrupt 8 | """ 9 | def __init__(self): 10 | self.name = 'most frequent' 11 | self.vars = ('interrupt',) 12 | self.type = 's' 13 | self.width = 20 14 | self.scale = 0 15 | self.intset1 = [ ] 16 | self.open('/proc/stat') 17 | self.names = self.names() 18 | 19 | def names(self): 20 | ret = {} 21 | for line in dopen('/proc/interrupts'): 22 | l = line.split() 23 | if len(l) <= cpunr: continue 24 | l1 = l[0].split(':')[0] 25 | ### Cleanup possible names from /proc/interrupts 26 | l2 = ' '.join(l[cpunr+3:]) 27 | l2 = l2.replace('_hcd:', '/') 28 | l2 = re.sub('@pci[:\d+\.]+', '', l2) 29 | l2 = re.sub('ahci\[[:\da-z\.]+\]', 'ahci', l2) 30 | ret[l1] = l2 31 | return ret 32 | 33 | def extract(self): 34 | self.output = '' 35 | self.val['total'] = 0.0 36 | for line in self.splitlines(): 37 | if line[0] == 'intr': 38 | self.intset2 = [ int(i) for i in line[3:] ] 39 | 40 | if not self.intset1: 41 | self.intset1 = [ 0 for i in self.intset2 ] 42 | 43 | for i in range(len(self.intset2)): 44 | total = (self.intset2[i] - self.intset1[i]) * 1.0 / elapsed 45 | 46 | ### Put the highest value in self.val 47 | if total > self.val['total']: 48 | if str(i+1) in self.names: 49 | self.val['name'] = self.names[str(i+1)] 50 | else: 51 | self.val['name'] = 'int ' + str(i+1) 52 | self.val['total'] = total 53 | 54 | if step == op.delay: 55 | self.intset1 = self.intset2 56 | 57 | if self.val['total'] != 0.0: 58 | self.output = '%-15s%s' % (self.val['name'], cprint(self.val['total'], 'd', 5, 1000)) 59 | 60 | def showcsv(self): 61 | return '%s / %f' % (self.val['name'], self.val['total']) 62 | 63 | # vim:ts=4:sw=4:et 64 | -------------------------------------------------------------------------------- /plugins/dstat_top_io.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | """ 5 | Top most expensive I/O process 6 | 7 | Displays the name of the most expensive I/O process 8 | """ 9 | def __init__(self): 10 | self.name = 'most expensive' 11 | self.vars = ('i/o process',) 12 | self.type = 's' 13 | self.width = 22 14 | self.scale = 0 15 | self.pidset1 = {} 16 | 17 | def check(self): 18 | if not os.access('/proc/self/io', os.R_OK): 19 | raise Exception('Kernel has no per-process I/O accounting [CONFIG_TASK_IO_ACCOUNTING], use at least 2.6.20') 20 | 21 | def extract(self): 22 | self.output = '' 23 | self.pidset2 = {} 24 | self.val['usage'] = 0.0 25 | for pid in proc_pidlist(): 26 | try: 27 | ### Reset values 28 | if pid not in self.pidset2: 29 | self.pidset2[pid] = {'rchar:': 0, 'wchar:': 0} 30 | if pid not in self.pidset1: 31 | self.pidset1[pid] = {'rchar:': 0, 'wchar:': 0} 32 | 33 | ### Extract name 34 | name = proc_splitline('/proc/%s/stat' % pid)[1][1:-1] 35 | 36 | ### Extract counters 37 | for l in proc_splitlines('/proc/%s/io' % pid): 38 | if len(l) != 2: continue 39 | self.pidset2[pid][l[0]] = int(l[1]) 40 | except IOError: 41 | continue 42 | except IndexError: 43 | continue 44 | 45 | read_usage = (self.pidset2[pid]['rchar:'] - self.pidset1[pid]['rchar:']) * 1.0 / elapsed 46 | write_usage = (self.pidset2[pid]['wchar:'] - self.pidset1[pid]['wchar:']) * 1.0 / elapsed 47 | usage = read_usage + write_usage 48 | # if usage > 0.0: 49 | # print('%s %s:%s' % (pid, read_usage, write_usage)) 50 | 51 | ### Get the process that spends the most jiffies 52 | if usage > self.val['usage']: 53 | self.val['usage'] = usage 54 | self.val['read_usage'] = read_usage 55 | self.val['write_usage'] = write_usage 56 | self.val['pid'] = pid 57 | self.val['name'] = getnamebypid(pid, name) 58 | 59 | if step == op.delay: 60 | self.pidset1 = self.pidset2 61 | 62 | if self.val['usage'] != 0.0: 63 | self.output = '%-*s%s %s' % (self.width-11, self.val['name'][0:self.width-11], cprint(self.val['read_usage'], 'd', 5, 1024), cprint(self.val['write_usage'], 'd', 5, 1024)) 64 | 65 | ### Debug (show PID) 66 | # self.output = '%*s %-*s%s %s' % (5, self.val['pid'], self.width-17, self.val['name'][0:self.width-17], cprint(self.val['read_usage'], 'd', 5, 1024), cprint(self.val['write_usage'], 'd', 5, 1024)) 67 | 68 | def showcsv(self): 69 | return '%s / %d:%d' % (self.val['name'], self.val['read_usage'], self.val['write_usage']) 70 | 71 | # vim:ts=4:sw=4:et 72 | -------------------------------------------------------------------------------- /plugins/dstat_top_io_adv.py: -------------------------------------------------------------------------------- 1 | ### Dstat all I/O process plugin 2 | ### Displays all processes' I/O read/write stats and CPU usage 3 | ### 4 | ### Authority: Guillermo Cantu Luna 5 | 6 | class dstat_plugin(dstat): 7 | def __init__(self): 8 | self.name = 'most expensive i/o process' 9 | self.vars = ('process pid read write cpu',) 10 | self.type = 's' 11 | self.width = 40 12 | self.scale = 0 13 | self.pidset1 = {} 14 | 15 | def check(self): 16 | if not os.access('/proc/self/io', os.R_OK): 17 | raise Exception('Kernel has no per-process I/O accounting [CONFIG_TASK_IO_ACCOUNTING], use at least 2.6.20') 18 | return True 19 | 20 | def extract(self): 21 | self.output = '' 22 | self.pidset2 = {} 23 | self.val['usage'] = 0.0 24 | for pid in proc_pidlist(): 25 | try: 26 | ### Reset values 27 | if pid not in self.pidset2: 28 | self.pidset2[pid] = {'rchar:': 0, 'wchar:': 0, 'cputime:': 0, 'cpuper:': 0} 29 | if pid not in self.pidset1: 30 | self.pidset1[pid] = {'rchar:': 0, 'wchar:': 0, 'cputime:': 0, 'cpuper:': 0} 31 | 32 | ### Extract name 33 | name = proc_splitline('/proc/%s/stat' % pid)[1][1:-1] 34 | 35 | ### Extract counters 36 | for l in proc_splitlines('/proc/%s/io' % pid): 37 | if len(l) != 2: continue 38 | self.pidset2[pid][l[0]] = int(l[1]) 39 | 40 | ### Get CPU usage 41 | l = proc_splitline('/proc/%s/stat' % pid) 42 | if len(l) < 15: 43 | cpu_usage = 0 44 | else: 45 | self.pidset2[pid]['cputime:'] = int(l[13]) + int(l[14]) 46 | cpu_usage = (self.pidset2[pid]['cputime:'] - self.pidset1[pid]['cputime:']) * 1.0 / elapsed / cpunr 47 | 48 | except ValueError: 49 | continue 50 | except IOError: 51 | continue 52 | except IndexError: 53 | continue 54 | 55 | read_usage = (self.pidset2[pid]['rchar:'] - self.pidset1[pid]['rchar:']) * 1.0 / elapsed 56 | write_usage = (self.pidset2[pid]['wchar:'] - self.pidset1[pid]['wchar:']) * 1.0 / elapsed 57 | usage = read_usage + write_usage 58 | 59 | ### Get the process that spends the most jiffies 60 | if usage > self.val['usage']: 61 | self.val['usage'] = usage 62 | self.val['read_usage'] = read_usage 63 | self.val['write_usage'] = write_usage 64 | self.val['pid'] = pid 65 | self.val['name'] = getnamebypid(pid, name) 66 | self.val['cpu_usage'] = cpu_usage 67 | 68 | if step == op.delay: 69 | self.pidset1 = self.pidset2 70 | 71 | if self.val['usage'] != 0.0: 72 | self.output = '%-*s%s%-5s%s%s%s%s%%' % (self.width-14-len(pid), self.val['name'][0:self.width-14-len(pid)], color['darkblue'], self.val['pid'], cprint(self.val['read_usage'], 'd', 5, 1024), cprint(self.val['write_usage'], 'd', 5, 1024), cprint(self.val['cpu_usage'], 'f', 3, 34), color['darkgray']) 73 | 74 | def showcsv(self): 75 | return 'Top: %s\t%s\t%s\t%s' % (self.val['name'][0:self.width-20], self.val['read_usage'], self.val['write_usage'], self.val['cpu_usage']) 76 | -------------------------------------------------------------------------------- /plugins/dstat_top_latency.py: -------------------------------------------------------------------------------- 1 | ### Authority: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | """ 5 | Top process with highest total latency. 6 | 7 | Displays name and total amount of CPU time waited in milliseconds of 8 | the process that has the highest total amount waited for the measured 9 | timeframe. 10 | 11 | For more information see: 12 | 13 | http://eaglet.rain.com/rick/linux/schedstat/ 14 | """ 15 | 16 | def __init__(self): 17 | self.name = 'highest total' 18 | self.vars = ('latency process',) 19 | self.type = 's' 20 | self.width = 17 21 | self.scale = 0 22 | self.pidset1 = {} 23 | 24 | def check(self): 25 | if not os.access('/proc/self/schedstat', os.R_OK): 26 | raise Exception('Kernel has no scheduler statistics [CONFIG_SCHEDSTATS], use at least 2.6.12') 27 | 28 | def extract(self): 29 | self.output = '' 30 | self.pidset2 = {} 31 | self.val['result'] = 0 32 | for pid in proc_pidlist(): 33 | try: 34 | ### Reset values 35 | if pid not in self.pidset1: 36 | self.pidset1[pid] = {'wait_ticks': 0} 37 | 38 | ### Extract name 39 | name = proc_splitline('/proc/%s/stat' % pid)[1][1:-1] 40 | 41 | ### Extract counters 42 | l = proc_splitline('/proc/%s/schedstat' % pid) 43 | except IOError: 44 | continue 45 | except IndexError: 46 | continue 47 | 48 | if len(l) != 3: continue 49 | 50 | self.pidset2[pid] = {'wait_ticks': int(l[1])} 51 | 52 | totwait = (self.pidset2[pid]['wait_ticks'] - self.pidset1[pid]['wait_ticks']) * 1.0 / elapsed 53 | 54 | ### Get the process that spends the most jiffies 55 | if totwait > self.val['result']: 56 | self.val['result'] = totwait 57 | self.val['pid'] = pid 58 | self.val['name'] = getnamebypid(pid, name) 59 | 60 | if step == op.delay: 61 | self.pidset1 = self.pidset2 62 | 63 | if self.val['result'] != 0.0: 64 | self.output = '%-*s%s' % (self.width-4, self.val['name'][0:self.width-4], cprint(self.val['result'], 'd', 4, 100)) 65 | 66 | ### Debug (show PID) 67 | # self.output = '%*s %-*s' % (5, self.val['pid'], self.width-6, self.val['name']) 68 | 69 | def showcsv(self): 70 | return '%s / %.4f' % (self.val['name'], self.val['result']) 71 | 72 | # vim:ts=4:sw=4:et 73 | -------------------------------------------------------------------------------- /plugins/dstat_top_latency_avg.py: -------------------------------------------------------------------------------- 1 | ### Dstat most expensive I/O process plugin 2 | ### Displays the name of the most expensive I/O process 3 | ### 4 | ### Authority: dag@wieers.com 5 | 6 | ### For more information, see: 7 | ### http://eaglet.rain.com/rick/linux/schedstat/ 8 | 9 | class dstat_plugin(dstat): 10 | def __init__(self): 11 | self.name = 'highest average' 12 | self.vars = ('latency process',) 13 | self.type = 's' 14 | self.width = 17 15 | self.scale = 0 16 | self.pidset1 = {} 17 | 18 | def check(self): 19 | if not os.access('/proc/self/schedstat', os.R_OK): 20 | raise Exception('Kernel has no scheduler statistics [CONFIG_SCHEDSTATS], use at least 2.6.12') 21 | 22 | def extract(self): 23 | self.output = '' 24 | self.pidset2 = {} 25 | self.val['result'] = 0 26 | for pid in proc_pidlist(): 27 | try: 28 | ### Reset values 29 | if pid not in self.pidset1: 30 | self.pidset1[pid] = {'wait_ticks': 0, 'ran': 0} 31 | 32 | ### Extract name 33 | name = proc_splitline('/proc/%s/stat' % pid)[1][1:-1] 34 | 35 | ### Extract counters 36 | l = proc_splitline('/proc/%s/schedstat' % pid) 37 | except IOError: 38 | continue 39 | except IndexError: 40 | continue 41 | 42 | if len(l) != 3: continue 43 | 44 | self.pidset2[pid] = {'wait_ticks': int(l[1]), 'ran': int(l[2])} 45 | 46 | if self.pidset2[pid]['ran'] - self.pidset1[pid]['ran'] > 0: 47 | avgwait = (self.pidset2[pid]['wait_ticks'] - self.pidset1[pid]['wait_ticks']) * 1.0 / (self.pidset2[pid]['ran'] - self.pidset1[pid]['ran']) / elapsed 48 | else: 49 | avgwait = 0 50 | 51 | ### Get the process that spends the most jiffies 52 | if avgwait > self.val['result']: 53 | self.val['result'] = avgwait 54 | self.val['pid'] = pid 55 | self.val['name'] = getnamebypid(pid, name) 56 | 57 | if step == op.delay: 58 | self.pidset1 = self.pidset2 59 | 60 | if self.val['result'] != 0.0: 61 | self.output = '%-*s%s' % (self.width-4, self.val['name'][0:self.width-4], cprint(self.val['result'], 'f', 4, 100)) 62 | 63 | ### Debug (show PID) 64 | # self.output = '%*s %-*s' % (5, self.val['pid'], self.width-6, self.val['name']) 65 | 66 | def showcsv(self): 67 | return '%s / %.4f' % (self.val['name'], self.val['result']) 68 | 69 | # vim:ts=4:sw=4:et 70 | -------------------------------------------------------------------------------- /plugins/dstat_top_mem.py: -------------------------------------------------------------------------------- 1 | ### Authority: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | """ 5 | Most expensive CPU process. 6 | 7 | Displays the process that uses the CPU the most during the monitored 8 | interval. The value displayed is the percentage of CPU time for the total 9 | amount of CPU processing power. Based on per process CPU information. 10 | """ 11 | def __init__(self): 12 | self.name = 'most expensive' 13 | self.vars = ('memory process',) 14 | self.type = 's' 15 | self.width = 17 16 | self.scale = 0 17 | 18 | def extract(self): 19 | self.val['max'] = 0.0 20 | for pid in proc_pidlist(): 21 | try: 22 | ### Using dopen() will cause too many open files 23 | l = proc_splitline('/proc/%s/stat' % pid) 24 | except IOError: 25 | continue 26 | 27 | if len(l) < 23: continue 28 | usage = int(l[23]) * pagesize 29 | 30 | ### Is it a new topper ? 31 | if usage <= self.val['max']: continue 32 | 33 | self.val['max'] = usage 34 | self.val['name'] = getnamebypid(pid, l[1][1:-1]) 35 | self.val['pid'] = pid 36 | 37 | self.output = '%-*s%s' % (self.width-5, self.val['name'][0:self.width-5], cprint(self.val['max'], 'f', 5, 1024)) 38 | 39 | ### Debug (show PID) 40 | # self.val['memory process'] = '%*s %-*s' % (5, self.val['pid'], self.width-6, self.val['name']) 41 | 42 | def showcsv(self): 43 | return '%s / %d%%' % (self.val['name'], self.val['max']) 44 | 45 | # vim:ts=4:sw=4:et 46 | -------------------------------------------------------------------------------- /plugins/dstat_top_oom.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | ### Dstat most expensive process plugin 4 | ### Displays the name of the most expensive process 5 | 6 | ### More information: 7 | ### http://lwn.net/Articles/317814/ 8 | 9 | class dstat_plugin(dstat): 10 | def __init__(self): 11 | self.name = 'out of memory' 12 | self.vars = ('kill score',) 13 | self.type = 's' 14 | self.width = 18 15 | self.scale = 0 16 | 17 | def check(self): 18 | if not os.access('/proc/self/oom_score', os.R_OK): 19 | raise Exception('Kernel does not support /proc/pid/oom_score, use at least 2.6.11.') 20 | 21 | def extract(self): 22 | self.output = '' 23 | self.val['max'] = 0.0 24 | for pid in proc_pidlist(): 25 | try: 26 | ### Extract name 27 | name = proc_splitline('/proc/%s/stat' % pid)[1][1:-1] 28 | 29 | ### Using dopen() will cause too many open files 30 | l = proc_splitline('/proc/%s/oom_score' % pid) 31 | except IOError: 32 | continue 33 | except IndexError: 34 | continue 35 | 36 | if len(l) < 1: continue 37 | oom_score = int(l[0]) 38 | 39 | ### Is it a new topper ? 40 | if oom_score <= self.val['max']: continue 41 | 42 | self.val['max'] = oom_score 43 | self.val['name'] = getnamebypid(pid, name) 44 | self.val['pid'] = pid 45 | 46 | if self.val['max'] != 0.0: 47 | self.output = '%-*s%s' % (self.width-4, self.val['name'][0:self.width-4], cprint(self.val['max'], 'f', 4, 1000)) 48 | 49 | ### Debug (show PID) 50 | # self.output = '%*s %-*s' % (5, self.val['pid'], self.width-6, self.val['name']) 51 | 52 | def showcsv(self): 53 | return '%s / %d%%' % (self.val['name'], self.val['max']) 54 | 55 | # vim:ts=4:sw=4:et 56 | -------------------------------------------------------------------------------- /plugins/dstat_utmp.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | def __init__(self): 5 | self.name = 'utmp' 6 | self.nick = ('ses', 'usr', 'adm' ) 7 | self.vars = ('sessions', 'users', 'root') 8 | self.type = 'd' 9 | self.width = 3 10 | self.scale = 10 11 | 12 | def check(self): 13 | try: 14 | global utmp 15 | import utmp 16 | except: 17 | raise Exception('Needs python-utmp module') 18 | 19 | def extract(self): 20 | for name in self.vars: self.val[name] = 0 21 | for u in utmp.UtmpRecord(): 22 | # print('# type:%s pid:%s line:%s id:%s user:%s host:%s session:%s' % (i.ut_type, i.ut_pid, i.ut_line, i.ut_id, i.ut_user, i.ut_host, i.ut_session)) 23 | if u.ut_type == utmp.USER_PROCESS: 24 | self.val['users'] = self.val['users'] + 1 25 | if u.ut_user == 'root': 26 | self.val['root'] = self.val['root'] + 1 27 | self.val['sessions'] = self.val['sessions'] + 1 28 | 29 | # vim:ts=4:sw=4:et 30 | -------------------------------------------------------------------------------- /plugins/dstat_vm_cpu.py: -------------------------------------------------------------------------------- 1 | ### Author: Bert de Bruijn 2 | 3 | ### VMware cpu stats 4 | ### Displays CPU stats coming from the hypervisor inside VMware VMs. 5 | ### The vmGuestLib API from VMware Tools needs to be installed 6 | 7 | class dstat_plugin(dstat): 8 | def __init__(self): 9 | self.name = 'vm cpu' 10 | self.vars = ('used', 'stolen', 'elapsed') 11 | self.nick = ('usd', 'stl') 12 | self.type = 'p' 13 | self.width = 3 14 | self.scale = 100 15 | self.cpunr = getcpunr() 16 | 17 | def check(self): 18 | try: 19 | global vmguestlib 20 | import vmguestlib 21 | 22 | self.gl = vmguestlib.VMGuestLib() 23 | except: 24 | raise Exception('Needs python-vmguestlib module') 25 | 26 | def extract(self): 27 | self.gl.UpdateInfo() 28 | self.set2['elapsed'] = self.gl.GetElapsedMs() 29 | self.set2['stolen'] = self.gl.GetCpuStolenMs() 30 | self.set2['used'] = self.gl.GetCpuUsedMs() 31 | 32 | for name in ('stolen', 'used'): 33 | self.val[name] = (self.set2[name] - self.set1[name]) * 100 / (self.set2['elapsed'] - self.set1['elapsed']) / self.cpunr 34 | 35 | if step == op.delay: 36 | self.set1.update(self.set2) 37 | 38 | # vim:ts=4:sw=4 39 | -------------------------------------------------------------------------------- /plugins/dstat_vm_mem.py: -------------------------------------------------------------------------------- 1 | ### Author: Bert de Bruijn 2 | 3 | ### VMware memory stats 4 | ### Displays memory stats coming from the hypervisor inside VMware VMs. 5 | ### The vmGuestLib API from VMware Tools needs to be installed 6 | 7 | class dstat_plugin(dstat): 8 | def __init__(self): 9 | self.name = 'vmware memory' 10 | self.vars = ('active', 'ballooned', 'mapped', 'swapped', 'used') 11 | self.nick = ('active', 'balln', 'mappd', 'swapd', 'used') 12 | self.type = 'd' 13 | self.width = 5 14 | self.scale = 1024 15 | 16 | def check(self): 17 | try: 18 | global vmguestlib 19 | import vmguestlib 20 | 21 | self.gl = vmguestlib.VMGuestLib() 22 | except: 23 | raise Exception('Needs python-vmguestlib module') 24 | 25 | def extract(self): 26 | self.gl.UpdateInfo() 27 | self.val['active'] = self.gl.GetMemActiveMB() * 1024 ** 2 28 | self.val['ballooned'] = self.gl.GetMemBalloonedMB() * 1024 ** 2 29 | self.val['mapped'] = self.gl.GetMemMappedMB() * 1024 ** 2 30 | self.val['swapped'] = self.gl.GetMemSwappedMB() * 1024 ** 2 31 | self.val['used'] = self.gl.GetMemUsedMB() * 1024 ** 2 32 | 33 | # vim:ts=4:sw=4 34 | -------------------------------------------------------------------------------- /plugins/dstat_vm_mem_adv.py: -------------------------------------------------------------------------------- 1 | ### Author: Bert de Bruijn 2 | 3 | ### VMware advanced memory stats 4 | ### Displays memory stats coming from the hypervisor inside VMware VMs. 5 | ### The vmGuestLib API from VMware Tools needs to be installed 6 | 7 | class dstat_plugin(dstat): 8 | def __init__(self): 9 | self.name = 'vmware advanced memory' 10 | self.vars = ('active', 'ballooned', 'mapped', 'overhead', 'saved', 'shared', 'swapped', 'targetsize', 'used') 11 | self.nick = ('active', 'balln', 'mappd', 'ovrhd', 'saved', 'shard', 'swapd', 'targt', 'used') 12 | self.type = 'd' 13 | self.width = 5 14 | self.scale = 1024 15 | 16 | def check(self): 17 | try: 18 | global vmguestlib 19 | import vmguestlib 20 | 21 | self.gl = vmguestlib.VMGuestLib() 22 | except: 23 | raise Exception('Needs python-vmguestlib module') 24 | 25 | def extract(self): 26 | self.gl.UpdateInfo() 27 | self.val['active'] = self.gl.GetMemActiveMB() * 1024 ** 2 28 | self.val['ballooned'] = self.gl.GetMemBalloonedMB() * 1024 ** 2 29 | self.val['mapped'] = self.gl.GetMemMappedMB() * 1024 ** 2 30 | self.val['overhead'] = self.gl.GetMemOverheadMB() * 1024 ** 2 31 | self.val['saved'] = self.gl.GetMemSharedSavedMB() * 1024 ** 2 32 | self.val['shared'] = self.gl.GetMemSharedMB() * 1024 ** 2 33 | self.val['swapped'] = self.gl.GetMemSwappedMB() * 1024 ** 2 34 | self.val['targetsize'] = self.gl.GetMemTargetSizeMB() * 1024 ** 2 35 | self.val['used'] = self.gl.GetMemUsedMB() * 1024 ** 2 36 | 37 | # vim:ts=4:sw=4 38 | -------------------------------------------------------------------------------- /plugins/dstat_vmk_hba.py: -------------------------------------------------------------------------------- 1 | ### Author: Bert de Bruijn 2 | 3 | ### VMware ESX kernel vmhba stats 4 | ### Displays kernel vmhba statistics on VMware ESX servers 5 | 6 | # NOTE TO USERS: command-line plugin configuration is not yet possible, so I've 7 | # "borrowed" the -D argument. 8 | # EXAMPLES: 9 | # # dstat --vmkhba -D vmhba1,vmhba2,total 10 | # # dstat --vmkhba -D vmhba0 11 | # You can even combine the Linux and VMkernel diskstats (but the "total" argument 12 | # will be used by both). 13 | # # dstat --vmkhba -d -D sda,vmhba1 14 | 15 | class dstat_plugin(dstat): 16 | def __init__(self): 17 | self.name = 'vmkhba' 18 | self.nick = ('read', 'writ') 19 | self.cols = 2 20 | 21 | def discover(self, *list): 22 | # discover will list all vmhba's found. 23 | # we might want to filter out the unused vmhba's (read stats, compare with ['0', ] * 13) 24 | ret = [] 25 | try: 26 | list = os.listdir('/proc/vmware/scsi/') 27 | except: 28 | raise Exception('Needs VMware ESX') 29 | for name in list: 30 | for line in dopen('/proc/vmware/scsi/%s/stats' % name).readlines(): 31 | l = line.split() 32 | if len(l) < 13: continue 33 | if l[0] == 'cmds': continue 34 | if l == ['0', ] * 13: continue 35 | ret.append(name) 36 | return ret 37 | 38 | def vars(self): 39 | # vars will take the argument list - when implemented - , use total, or will use discover + total 40 | ret = [] 41 | if op.disklist: 42 | list = op.disklist 43 | #elif not op.full: 44 | # list = ('total', ) 45 | else: 46 | list = self.discover 47 | list.sort() 48 | for name in list: 49 | if name in self.discover + ['total']: 50 | ret.append(name) 51 | return ret 52 | 53 | def check(self): 54 | try: 55 | os.listdir('/proc/vmware') 56 | except: 57 | raise Exception('Needs VMware ESX') 58 | info(1, 'The vmkhba module is an EXPERIMENTAL module.') 59 | 60 | def extract(self): 61 | self.set2['total'] = (0, 0) 62 | for name in self.vars: 63 | self.set2[name] = (0, 0) 64 | for name in os.listdir('/proc/vmware/scsi/'): 65 | for line in dopen('/proc/vmware/scsi/%s/stats' % name).readlines(): 66 | l = line.split() 67 | if len(l) < 13: continue 68 | if l[0] == 'cmds': continue 69 | if l[2] == '0' and l[4] == '0': continue 70 | if l == ['0', ] * 13: continue 71 | self.set2['total'] = ( self.set2['total'][0] + int(l[2]), self.set2['total'][1] + int(l[4]) ) 72 | if name in self.vars and name != 'total': 73 | self.set2[name] = ( int(l[2]), int(l[4]) ) 74 | 75 | for name in self.set2: 76 | self.val[name] = list(map(lambda x, y: (y - x) * 1024.0 / elapsed, self.set1[name], self.set2[name])) 77 | 78 | if step == op.delay: 79 | self.set1.update(self.set2) 80 | -------------------------------------------------------------------------------- /plugins/dstat_vmk_int.py: -------------------------------------------------------------------------------- 1 | ### Author: Bert de Bruijn 2 | 3 | ### VMware ESX kernel interrupt stats 4 | ### Displays kernel interrupt statistics on VMware ESX servers 5 | 6 | # NOTE TO USERS: command-line plugin configuration is not yet possible, so I've 7 | # "borrowed" the -I argument. 8 | # EXAMPLES: 9 | # # dstat --vmkint -I 0x46,0x5a 10 | # You can even combine the Linux and VMkernel interrupt stats 11 | # # dstat --vmkint -i -I 14,0x5a 12 | # Look at /proc/vmware/interrupts to see which interrupt is linked to which function 13 | 14 | class dstat_plugin(dstat): 15 | def __init__(self): 16 | self.name = 'vmkint' 17 | self.type = 'd' 18 | self.width = 4 19 | self.scale = 1000 20 | self.open('/proc/vmware/interrupts') 21 | # self.intmap = self.intmap() 22 | 23 | # def intmap(self): 24 | # ret = {} 25 | # for line in dopen('/proc/vmware/interrupts').readlines(): 26 | # l = line.split() 27 | # if len(l) <= self.vmkcpunr: continue 28 | # l1 = l[0].split(':')[0] 29 | # l2 = ' '.join(l[vmkcpunr()+1:]).split(',') 30 | # ret[l1] = l1 31 | # for name in l2: 32 | # ret[name.strip().lower()] = l1 33 | # return ret 34 | 35 | def vmkcpunr(self): 36 | #the service console sees only one CPU, so cpunr == 1, only the vmkernel sees all CPUs 37 | ret = [] 38 | # default cpu number is 2 39 | ret = 2 40 | for l in self.fd[0].splitlines(): 41 | if l[0] == 'Vector': 42 | ret = int( int( l[-1] ) + 1 ) 43 | return ret 44 | 45 | def discover(self): 46 | #interrupt names are not decimal numbers, but rather hexadecimal numbers like 0x7e 47 | ret = [] 48 | self.fd[0].seek(0) 49 | for line in self.fd[0].readlines(): 50 | l = line.split() 51 | if l[0] == 'Vector': continue 52 | if len(l) < self.vmkcpunr()+1: continue 53 | name = l[0].split(':')[0] 54 | amount = 0 55 | for i in l[1:1+self.vmkcpunr()]: 56 | amount = amount + int(i) 57 | if amount > 20: ret.append(str(name)) 58 | return ret 59 | 60 | def vars(self): 61 | ret = [] 62 | if op.intlist: 63 | list = op.intlist 64 | else: 65 | list = self.discover 66 | # len(list) > 5: list = list[-5:] 67 | for name in list: 68 | if name in self.discover: 69 | ret.append(name) 70 | # elif name.lower() in self.intmap: 71 | # ret.append(self.intmap[name.lower()]) 72 | return ret 73 | 74 | def check(self): 75 | try: 76 | os.listdir('/proc/vmware') 77 | except: 78 | raise Exception('Needs VMware ESX') 79 | info(1, 'The vmkint module is an EXPERIMENTAL module.') 80 | 81 | def extract(self): 82 | self.fd[0].seek(0) 83 | for line in self.fd[0].readlines(): 84 | l = line.split() 85 | if len(l) < self.vmkcpunr()+1: continue 86 | name = l[0].split(':')[0] 87 | if name in self.vars: 88 | self.set2[name] = 0 89 | for i in l[1:1+self.vmkcpunr()]: 90 | self.set2[name] = self.set2[name] + int(i) 91 | 92 | for name in self.set2: 93 | self.val[name] = (self.set2[name] - self.set1[name]) * 1.0 / elapsed 94 | 95 | if step == op.delay: 96 | self.set1.update(self.set2) 97 | 98 | # vim:ts=4:sw=4 99 | -------------------------------------------------------------------------------- /plugins/dstat_vmk_nic.py: -------------------------------------------------------------------------------- 1 | ### Author: Bert de Bruijn 2 | 3 | ### VMware ESX kernel vmknic stats 4 | ### Displays VMkernel port statistics on VMware ESX servers 5 | 6 | # NOTE TO USERS: command-line plugin configuration is not yet possible, so I've 7 | # "borrowed" the -N argument. 8 | # EXAMPLES: 9 | # # dstat --vmknic -N vmk1 10 | # You can even combine the Linux and VMkernel network stats (just don't just "total"). 11 | # # dstat --vmknic -n -N vmk0,vswif0 12 | # NB Data comes from /proc/vmware/net/tcpip/ifconfig 13 | 14 | class dstat_plugin(dstat): 15 | def __init__(self): 16 | self.name = 'vmknic' 17 | self.nick = ('recv', 'send') 18 | self.open('/proc/vmware/net/tcpip/ifconfig') 19 | self.cols = 2 20 | 21 | def check(self): 22 | try: 23 | os.listdir('/proc/vmware') 24 | except: 25 | raise Exception('Needs VMware ESX') 26 | info(1, 'The vmknic module is an EXPERIMENTAL module.') 27 | 28 | def discover(self, *list): 29 | ret = [] 30 | for l in self.fd[0].splitlines(replace=' /', delim='/'): 31 | if len(l) != 12: continue 32 | if l[2][:5] == ' 2 | 3 | #Version: 2.2 4 | #VEID user nice system uptime idle strv uptime used maxlat totlat numsched 5 | #302 142926 0 10252 152896388 852779112954062 0 427034187248480 1048603937010 0 0 0 6 | #301 27188 0 7896 152899846 853267000490282 0 427043845492614 701812592320 0 0 0 7 | 8 | class dstat_plugin(dstat): 9 | def __init__(self): 10 | self.nick = ('usr', 'sys', 'idl', 'nic') 11 | self.type = 'p' 12 | self.width = 3 13 | self.scale = 34 14 | self.open('/proc/vz/vestat') 15 | self.cols = 4 16 | 17 | def check(self): 18 | info(1, 'Module %s is still experimental.' % self.filename) 19 | 20 | def discover(self, *list): 21 | ret = [] 22 | for l in self.splitlines(): 23 | if len(l) < 6 or l[0] == 'VEID': continue 24 | ret.append(l[0]) 25 | ret.sort() 26 | for item in list: ret.append(item) 27 | return ret 28 | 29 | def name(self): 30 | ret = [] 31 | for name in self.vars: 32 | if name == 'total': 33 | ret.append('total ve usage') 34 | else: 35 | ret.append('ve ' + name + ' usage') 36 | return ret 37 | 38 | def vars(self): 39 | ret = [] 40 | if not op.full: 41 | list = ('total', ) 42 | else: 43 | list = self.discover 44 | for name in list: 45 | if name in self.discover + ['total']: 46 | ret.append(name) 47 | return ret 48 | 49 | def extract(self): 50 | self.set2['total'] = [0, 0, 0, 0] 51 | for l in self.splitlines(): 52 | if len(l) < 6 or l[0] == 'VEID': continue 53 | name = l[0] 54 | self.set2[name] = ( int(l[1]), int(l[3]), int(l[4]) - int(l[1]) - int(l[2]) - int(l[3]), int(l[2]) ) 55 | self.set2['total'] = ( self.set2['total'][0] + int(l[1]), self.set2['total'][1] + int(l[3]), self.set2['total'][2] + int(l[4]) - int(l[1]) - int(l[2]) - int(l[3]), self.set2['total'][3] + int(l[2]) ) 56 | 57 | for name in self.vars: 58 | for i in range(self.cols): 59 | self.val[name][i] = 100.0 * (self.set2[name][i] - self.set1[name][i]) / (sum(self.set2[name]) - sum(self.set1[name])) 60 | 61 | if step == op.delay: 62 | self.set1.update(self.set2) 63 | 64 | # vim:ts=4:sw=4:et 65 | -------------------------------------------------------------------------------- /plugins/dstat_vz_io.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | ### Example content for /proc/bc//ioacct 4 | # read 2773011640320 5 | # write 2095707136000 6 | # dirty 4500342390784 7 | # cancel 4080624041984 8 | # missed 0 9 | # syncs_total 2 10 | # fsyncs_total 1730732 11 | # fdatasyncs_total 3266 12 | # range_syncs_total 0 13 | # syncs_active 0 14 | # fsyncs_active 0 15 | # fdatasyncs_active 0 16 | # range_syncs_active 0 17 | # vfs_reads 3717331387 18 | # vfs_read_chars 3559144863185798078 19 | # vfs_writes 901216138 20 | # vfs_write_chars 23864660931174682 21 | # io_pbs 16 22 | 23 | class dstat_plugin(dstat): 24 | def __init__(self): 25 | self.nick = ['read', 'write', 'dirty', 'cancel', 'missed'] 26 | self.cols = len(self.nick) 27 | 28 | def check(self): 29 | if not os.path.exists('/proc/vz'): 30 | raise Exception('System does not have OpenVZ support') 31 | elif not os.path.exists('/proc/bc'): 32 | raise Exception('System does not have (new) OpenVZ beancounter support') 33 | elif not glob.glob('/proc/bc/*/ioacct'): 34 | raise Exception('System does not have any OpenVZ containers') 35 | info(1, 'Module %s is still experimental.' % self.filename) 36 | 37 | def name(self): 38 | return ['ve/'+name for name in self.vars] 39 | 40 | def vars(self): 41 | ret = [] 42 | if not op.full: 43 | varlist = ['total',] 44 | else: 45 | varlist = [os.path.basename(veid) for veid in glob.glob('/proc/vz/*')] 46 | ret = varlist 47 | return ret 48 | 49 | def extract(self): 50 | for name in self.vars: 51 | self.set2['total'] = {} 52 | for line in dopen('/proc/bc/%s/ioacct' % name).readlines(): 53 | l = line.split() 54 | if len(l) != 2: continue 55 | if l[0] not in self.nick: continue 56 | index = self.nick.index(l[0]) 57 | self.set2[name][index] = int(l[1]) 58 | self.set2['total'][index] = self.set2['total'][index] + int(l[1]) 59 | # print(name, self.val[name], self.set2[name][0], self.set2[name][1]) 60 | # print(name, self.val[name], self.set1[name][0], self.set1[name][1]) 61 | 62 | self.val[name] = list(map(lambda x, y: (y - x) / elapsed, self.set1[name], self.set2[name])) 63 | 64 | if step == op.delay: 65 | self.set1.update(self.set2) 66 | 67 | # vim:ts=4:sw=4:et 68 | -------------------------------------------------------------------------------- /plugins/dstat_vz_ubc.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | def __init__(self): 5 | self.nick = ('fcnt', ) 6 | self.type = 'd' 7 | self.width = 5 8 | self.scale = 1000 9 | self.open('/proc/user_beancounters') 10 | self.cols = 1 ### Is this correct ? 11 | 12 | def check(self): 13 | info(1, 'Module %s is still experimental.' % self.filename) 14 | 15 | def discover(self, *list): 16 | ret = [] 17 | for l in self.splitlines(): 18 | if len(l) < 7 or l[0] in ('uid', '0:'): continue 19 | ret.append(l[0][0:-1]) 20 | ret.sort() 21 | for item in list: ret.append(item) 22 | return ret 23 | 24 | def name(self): 25 | ret = [] 26 | for name in self.vars: 27 | if name == 'total': 28 | ret.append('total failcnt') 29 | else: 30 | ret.append(name) 31 | return ret 32 | 33 | def vars(self): 34 | ret = [] 35 | if not op.full: 36 | list = ('total', ) 37 | else: 38 | list = self.discover 39 | for name in list: 40 | if name in self.discover + ['total']: 41 | ret.append(name) 42 | return ret 43 | 44 | def extract(self): 45 | for name in self.vars + ['total']: 46 | self.set2[name] = 0 47 | for l in self.splitlines(): 48 | if len(l) < 6 or l[0] == 'uid': 49 | continue 50 | elif len(l) == 7: 51 | name = l[0][0:-1] 52 | if name in self.vars: 53 | self.set2[name] = self.set2[name] + int(l[6]) 54 | self.set2['total'] = self.set2['total'] + int(l[6]) 55 | elif name == '0': 56 | continue 57 | else: 58 | if name in self.vars: 59 | self.set2[name] = self.set2[name] + int(l[5]) 60 | self.set2['total'] = self.set2['total'] + int(l[5]) 61 | 62 | for name in self.vars: 63 | self.val[name] = (self.set2[name] - self.set1[name]) * 1.0 / elapsed 64 | 65 | if step == op.delay: 66 | self.set1.update(self.set2) 67 | 68 | # vim:ts=4:sw=4:et 69 | -------------------------------------------------------------------------------- /plugins/dstat_wifi.py: -------------------------------------------------------------------------------- 1 | ### Author: Dag Wieers 2 | 3 | class dstat_plugin(dstat): 4 | def __init__(self): 5 | self.name = 'wifi' 6 | self.nick = ('lnk', 's/n') 7 | self.type = 'd' 8 | self.width = 3 9 | self.scale = 34 10 | self.cols = 2 11 | 12 | def check(self): 13 | global iwlibs 14 | from pythonwifi import iwlibs 15 | 16 | def vars(self): 17 | return iwlibs.getNICnames() 18 | 19 | def extract(self): 20 | for name in self.vars: 21 | wifi = iwlibs.Wireless(name) 22 | stat, qual, discard, missed_beacon = wifi.getStatistics() 23 | # print(qual.quality, qual.signallevel, qual.noiselevel) 24 | if qual.quality == 0 or qual.signallevel == -101 or qual.noiselevel == -101 or qual.signallevel == -256 or qual.noiselevel == -256: 25 | self.val[name] = ( -1, -1 ) 26 | else: 27 | self.val[name] = ( qual.quality, qual.signallevel * 100 / qual.noiselevel ) 28 | 29 | # vim:ts=4:sw=4:et 30 | -------------------------------------------------------------------------------- /plugins/dstat_zfs_arc.py: -------------------------------------------------------------------------------- 1 | class dstat_plugin(dstat): 2 | """ 3 | ZFS on Linux ARC (Adjustable Replacement Cache) 4 | 5 | Data is extracted from /proc/spl/kstat/zfs/arcstats 6 | """ 7 | def __init__(self): 8 | self.name = 'ZFS ARC' 9 | self.nick = ('mem', 'hit', 'miss', 'reads', 'hit%') 10 | self.vars = ('size', 'hits', 'misses', 'total', 'hit_rate') 11 | self.types = ('b', 'd', 'd', 'd', 'p') 12 | self.scales = (1024, 1000, 1000, 1000, 1000) 13 | self.counter = (False, True, True, False, False) 14 | self.open('/proc/spl/kstat/zfs/arcstats') 15 | 16 | def extract(self): 17 | for l in self.splitlines(): 18 | if len(l) < 2: continue 19 | l[0].split() 20 | name = l[0] 21 | if name in self.vars: 22 | self.set2[name] = int(l[2]) 23 | 24 | for i, name in enumerate (self.vars): 25 | if self.counter[i]: 26 | self.val[name] = (self.set2[name] - self.set1[name]) * 1.0 / elapsed 27 | else: 28 | self.val[name] = self.set2[name] 29 | 30 | self.val['total'] = self.val['hits'] + self.val['misses'] 31 | 32 | if self.val['total'] > 0 : 33 | self.val['hit_rate'] = self.val['hits'] / self.val['total'] * 100.0 34 | else: 35 | self.val['hit_rate'] = 0 36 | 37 | if step == op.delay: 38 | self.set1.update(self.set2) 39 | 40 | # vim:ts=4:sw=4:et 41 | -------------------------------------------------------------------------------- /plugins/dstat_zfs_l2arc.py: -------------------------------------------------------------------------------- 1 | class dstat_plugin(dstat): 2 | """ 3 | ZFS on Linux L2ARC (Level 2 Adjustable Replacement Cache) 4 | 5 | Data is extracted from /proc/spl/kstat/zfs/arcstats 6 | """ 7 | def __init__(self): 8 | self.name = 'ZFS L2ARC' 9 | self.nick = ('size', 'hit', 'miss', 'hit%', 'read', 'write') 10 | self.vars = ('l2_size', 'l2_hits', 'l2_misses', 'hit_rate', 'l2_read_bytes', 'l2_write_bytes') 11 | self.types = ('b', 'd', 'd', 'p', 'b', 'b') 12 | self.scales = (1024, 1000, 1000, 1000, 1024, 1024) 13 | self.counter = (False, True, True, False, True, True) 14 | self.open('/proc/spl/kstat/zfs/arcstats') 15 | 16 | def extract(self): 17 | for l in self.splitlines(): 18 | if len(l) < 2: continue 19 | l[0].split() 20 | name = l[0] 21 | if name in self.vars: 22 | self.set2[name] = int(l[2]) 23 | 24 | for i, name in enumerate (self.vars): 25 | if self.counter[i]: 26 | self.val[name] = (self.set2[name] - self.set1[name]) * 1.0 / elapsed 27 | else: 28 | self.val[name] = self.set2[name] 29 | 30 | probes = self.val['l2_hits'] + self.val['l2_misses'] 31 | 32 | if probes > 0 : 33 | self.val['hit_rate'] = self.val['l2_hits'] / probes * 100.0 34 | else: 35 | self.val['hit_rate'] = 0 36 | 37 | if step == op.delay: 38 | self.set1.update(self.set2) 39 | 40 | # vim:ts=4:sw=4:et 41 | -------------------------------------------------------------------------------- /plugins/dstat_zfs_zil.py: -------------------------------------------------------------------------------- 1 | class dstat_plugin(dstat): 2 | """ 3 | ZFS on Linux ZIL (ZFS Intent Log) 4 | 5 | Data is extracted from /proc/spl/kstat/zfs/zil 6 | """ 7 | def __init__(self): 8 | self.name = 'ZFS ZIL' 9 | self.nick = ('count', 'bytes') 10 | self.vars = ('zil_itx_metaslab_slog_count', 'zil_itx_metaslab_slog_bytes') 11 | self.types = ('d', 'b') 12 | self.scales = (1000, 1024) 13 | self.counter = (True, True) 14 | self.open('/proc/spl/kstat/zfs/zil') 15 | 16 | def extract(self): 17 | for l in self.splitlines(): 18 | if len(l) < 2: continue 19 | l[0].split() 20 | name = l[0] 21 | if name in self.vars: 22 | self.set2[name] = int(l[2]) 23 | 24 | for i, name in enumerate (self.vars): 25 | if self.counter[i]: 26 | self.val[name] = (self.set2[name] - self.set1[name]) * 1.0 / elapsed 27 | else: 28 | self.val[name] = self.set2[name] 29 | 30 | if step == op.delay: 31 | self.set1.update(self.set2) 32 | 33 | # vim:ts=4:sw=4:et 34 | -------------------------------------------------------------------------------- /proc/diskstats-2.6.11: -------------------------------------------------------------------------------- 1 | 1 0 ram0 0 0 0 0 0 0 0 0 0 0 0 2 | 1 1 ram1 0 0 0 0 0 0 0 0 0 0 0 3 | 1 2 ram2 0 0 0 0 0 0 0 0 0 0 0 4 | 1 3 ram3 0 0 0 0 0 0 0 0 0 0 0 5 | 1 4 ram4 0 0 0 0 0 0 0 0 0 0 0 6 | 1 5 ram5 0 0 0 0 0 0 0 0 0 0 0 7 | 1 6 ram6 0 0 0 0 0 0 0 0 0 0 0 8 | 1 7 ram7 0 0 0 0 0 0 0 0 0 0 0 9 | 1 8 ram8 0 0 0 0 0 0 0 0 0 0 0 10 | 1 9 ram9 0 0 0 0 0 0 0 0 0 0 0 11 | 1 10 ram10 0 0 0 0 0 0 0 0 0 0 0 12 | 1 11 ram11 0 0 0 0 0 0 0 0 0 0 0 13 | 1 12 ram12 0 0 0 0 0 0 0 0 0 0 0 14 | 1 13 ram13 0 0 0 0 0 0 0 0 0 0 0 15 | 1 14 ram14 0 0 0 0 0 0 0 0 0 0 0 16 | 1 15 ram15 0 0 0 0 0 0 0 0 0 0 0 17 | 3 0 hda 701516 942900 12172907 6225444 284041 770510 6545342 14685351 0 3033115 20954254 18 | 3 1 hda1 51 73 0 0 19 | 3 2 hda2 970571 970620 265871 265871 20 | 3 3 hda3 3 6 0 0 21 | 3 5 hda5 633013 10305474 647709 5181672 22 | 3 6 hda6 22653 181209 51758 414064 23 | 3 7 hda7 4505 538299 1 8 24 | 3 8 hda8 1075 32107 1 8 25 | 3 9 hda9 4685 80135 90117 683719 26 | 22 0 hdc 3699 183366 748408 154422 0 0 0 0 0 153383 154422 27 | 9 0 md0 0 0 0 0 0 0 0 0 0 0 0 28 | 253 0 dm-0 0 0 0 0 0 0 0 0 0 0 0 29 | 7 0 loop0 0 0 0 0 0 0 0 0 0 0 0 30 | 7 1 loop1 0 0 0 0 0 0 0 0 0 0 0 31 | 7 2 loop2 0 0 0 0 0 0 0 0 0 0 0 32 | 7 3 loop3 0 0 0 0 0 0 0 0 0 0 0 33 | 7 4 loop4 0 0 0 0 0 0 0 0 0 0 0 34 | 7 5 loop5 0 0 0 0 0 0 0 0 0 0 0 35 | 7 6 loop6 0 0 0 0 0 0 0 0 0 0 0 36 | 7 7 loop7 0 0 0 0 0 0 0 0 0 0 0 37 | 7 8 loop8 0 0 0 0 0 0 0 0 0 0 0 38 | 7 9 loop9 0 0 0 0 0 0 0 0 0 0 0 39 | 7 10 loop10 0 0 0 0 0 0 0 0 0 0 0 40 | 7 11 loop11 0 0 0 0 0 0 0 0 0 0 0 41 | 7 12 loop12 0 0 0 0 0 0 0 0 0 0 0 42 | 7 13 loop13 0 0 0 0 0 0 0 0 0 0 0 43 | 7 14 loop14 0 0 0 0 0 0 0 0 0 0 0 44 | 7 15 loop15 0 0 0 0 0 0 0 0 0 0 0 45 | 7 16 loop16 0 0 0 0 0 0 0 0 0 0 0 46 | 7 17 loop17 0 0 0 0 0 0 0 0 0 0 0 47 | 7 18 loop18 0 0 0 0 0 0 0 0 0 0 0 48 | 7 19 loop19 0 0 0 0 0 0 0 0 0 0 0 49 | 7 20 loop20 0 0 0 0 0 0 0 0 0 0 0 50 | 7 21 loop21 0 0 0 0 0 0 0 0 0 0 0 51 | 7 22 loop22 0 0 0 0 0 0 0 0 0 0 0 52 | 7 23 loop23 0 0 0 0 0 0 0 0 0 0 0 53 | 7 24 loop24 0 0 0 0 0 0 0 0 0 0 0 54 | 7 25 loop25 0 0 0 0 0 0 0 0 0 0 0 55 | 7 26 loop26 0 0 0 0 0 0 0 0 0 0 0 56 | 7 27 loop27 0 0 0 0 0 0 0 0 0 0 0 57 | 7 28 loop28 0 0 0 0 0 0 0 0 0 0 0 58 | 7 29 loop29 0 0 0 0 0 0 0 0 0 0 0 59 | 7 30 loop30 0 0 0 0 0 0 0 0 0 0 0 60 | 7 31 loop31 0 0 0 0 0 0 0 0 0 0 0 61 | 7 32 loop32 0 0 0 0 0 0 0 0 0 0 0 62 | 7 33 loop33 0 0 0 0 0 0 0 0 0 0 0 63 | 7 34 loop34 0 0 0 0 0 0 0 0 0 0 0 64 | 7 35 loop35 0 0 0 0 0 0 0 0 0 0 0 65 | 7 36 loop36 0 0 0 0 0 0 0 0 0 0 0 66 | 7 37 loop37 0 0 0 0 0 0 0 0 0 0 0 67 | 7 38 loop38 0 0 0 0 0 0 0 0 0 0 0 68 | 7 39 loop39 0 0 0 0 0 0 0 0 0 0 0 69 | 7 40 loop40 0 0 0 0 0 0 0 0 0 0 0 70 | 7 41 loop41 0 0 0 0 0 0 0 0 0 0 0 71 | 7 42 loop42 0 0 0 0 0 0 0 0 0 0 0 72 | 7 43 loop43 0 0 0 0 0 0 0 0 0 0 0 73 | 7 44 loop44 0 0 0 0 0 0 0 0 0 0 0 74 | 7 45 loop45 0 0 0 0 0 0 0 0 0 0 0 75 | 7 46 loop46 0 0 0 0 0 0 0 0 0 0 0 76 | 7 47 loop47 0 0 0 0 0 0 0 0 0 0 0 77 | 7 48 loop48 0 0 0 0 0 0 0 0 0 0 0 78 | 7 49 loop49 0 0 0 0 0 0 0 0 0 0 0 79 | 7 50 loop50 0 0 0 0 0 0 0 0 0 0 0 80 | 7 51 loop51 0 0 0 0 0 0 0 0 0 0 0 81 | 7 52 loop52 0 0 0 0 0 0 0 0 0 0 0 82 | 7 53 loop53 0 0 0 0 0 0 0 0 0 0 0 83 | 7 54 loop54 0 0 0 0 0 0 0 0 0 0 0 84 | 7 55 loop55 0 0 0 0 0 0 0 0 0 0 0 85 | 7 56 loop56 0 0 0 0 0 0 0 0 0 0 0 86 | 7 57 loop57 0 0 0 0 0 0 0 0 0 0 0 87 | 7 58 loop58 0 0 0 0 0 0 0 0 0 0 0 88 | 7 59 loop59 0 0 0 0 0 0 0 0 0 0 0 89 | 7 60 loop60 0 0 0 0 0 0 0 0 0 0 0 90 | 7 61 loop61 0 0 0 0 0 0 0 0 0 0 0 91 | 7 62 loop62 0 0 0 0 0 0 0 0 0 0 0 92 | 7 63 loop63 0 0 0 0 0 0 0 0 0 0 0 93 | -------------------------------------------------------------------------------- /proc/partitions-2.4.21: -------------------------------------------------------------------------------- 1 | major minor #blocks name rio rmerge rsect ruse wio wmerge wsect wuse running use aveq 2 | 3 | 22 0 20094480 hdc 18502 68378 614922 575620 63 16 632 660 -9 28571324 42702177 4 | 22 1 48163 hdc1 46 70 232 620 0 0 0 0 0 310 620 5 | 22 2 5116702 hdc2 15272 58372 588450 543550 63 16 632 660 0 31290 544240 6 | 22 3 5116702 hdc3 46 70 232 620 0 0 0 0 0 310 620 7 | 22 4 1 hdc4 0 0 0 0 0 0 0 0 0 0 0 8 | 22 5 5116671 hdc5 46 70 232 540 0 0 0 0 0 280 540 9 | 22 6 2562336 hdc6 46 70 232 610 0 0 0 0 0 310 610 10 | 22 7 514048 hdc7 46 70 232 580 0 0 0 0 0 290 580 11 | 22 8 265041 hdc8 58 98 312 360 0 0 0 0 0 180 360 12 | 22 9 1349428 hdc9 46 70 232 320 0 0 0 0 0 160 320 13 | 3 0 80043264 hda 165456390 118310028 -2026323812 11081595 319064897 471719179 2045961426 3973385 -6386 23654554 26608111 14 | 3 1 1020096 hda1 1022946 359030 11055850 13886300 18524433 42441169 487840520 19795761 0 15952587 33844701 15 | 3 2 33792727 hda2 16867355 41487758 466839562 3680943 49279950 136588121 1489574368 3787 0 7851978 6830330 16 | 3 3 20482875 hda3 21222823 25575094 374388250 40543161 47823459 36322836 675746654 7388934 0 15848238 6467323 17 | 3 4 1 hda4 0 0 0 0 0 0 0 0 0 0 0 18 | 3 5 19968763 hda5 12553421 16972432 236204218 36866208 132089301 133660978 2128837078 24946643 0 4783292 19031628 19 | 3 6 3068383 hda6 2219407 1764003 31864346 28167600 15941594 29654857 365639372 10253628 0 18729247 38579828 20 | 3 7 1020096 hda7 39200 370913 3278858 960450 939530 2371952 26950130 41566540 0 10284610 42535080 21 | 3 8 522081 hda8 111525236 31538692 1144516184 15779080 54466614 90679266 1166340568 28867099 0 7075683 3835907 22 | -------------------------------------------------------------------------------- /proc/partitions-2.4.24: -------------------------------------------------------------------------------- 1 | major minor #blocks name rio rmerge rsect ruse wio wmerge wsect wuse running use aveq 2 | 3 | 22 0 4224150 ide/host0/bus1/target0/lun0/disc 24958 39400 473150 343900 53587 23309 500312 5447730 0 380490 5795870 4 | 22 1 1023876 ide/host0/bus1/target0/lun0/part1 3592 4076 61498 43970 27109 5490 261392 546010 0 110060 590040 5 | 22 2 130882 ide/host0/bus1/target0/lun0/part2 1 0 8 10 0 0 0 0 0 10 10 6 | 22 3 2047815 ide/host0/bus1/target0/lun0/part3 16859 32873 397730 253060 21988 2699 199512 3131240 0 277340 3388480 7 | 22 4 1021545 ide/host0/bus1/target0/lun0/part4 4505 2448 13906 46850 4490 15120 39408 1770480 0 73390 1817330 8 | 3 0 8257032 ide/host0/bus0/target0/lun0/disc 1 3 8 10 0 0 0 0 0 10 10 9 | 3 1 2048256 ide/host0/bus0/target0/lun0/part1 0 0 0 0 0 0 0 0 0 0 0 10 | 3 2 3068415 ide/host0/bus0/target0/lun0/part2 0 0 0 0 0 0 0 0 0 0 0 11 | 3 3 1534207 ide/host0/bus0/target0/lun0/part3 0 0 0 0 0 0 0 0 0 0 0 12 | 3 4 1598467 ide/host0/bus0/target0/lun0/part4 0 0 0 0 0 0 0 0 0 0 0 13 | -------------------------------------------------------------------------------- /proc/partitions-2.6.11: -------------------------------------------------------------------------------- 1 | major minor #blocks name 2 | 3 | 3 0 78150744 hda 4 | 3 1 4611568 hda1 5 | 3 2 3591000 hda2 6 | 3 3 1 hda3 7 | 3 5 11801128 hda5 8 | 3 6 536728 hda6 9 | 3 7 6168928 hda7 10 | 3 8 6191608 hda8 11 | 3 9 45249592 hda9 12 | 253 0 4587520 dm-0 13 | 7 0 652862 loop0 14 | 7 1 653886 loop1 15 | 7 2 506174 loop2 16 | 7 3 631824 loop3 17 | 7 4 652852 loop4 18 | 7 5 651854 loop5 19 | 7 6 395278 loop6 20 | 7 7 652478 loop7 21 | 7 8 653444 loop8 22 | 7 9 652756 loop9 23 | 7 10 619972 loop10 24 | 7 11 137586 loop11 25 | 7 12 666538 loop12 26 | 7 13 659278 loop13 27 | 7 14 152052 loop14 28 | 7 15 157376 loop15 29 | 7 16 636162 loop16 30 | 7 17 649870 loop17 31 | 7 18 201976 loop18 32 | 7 19 102298 loop19 33 | 7 20 596550 loop20 34 | 7 21 250010 loop21 35 | 7 22 4332 loop22 36 | 7 23 47104 loop23 37 | 7 24 145808 loop24 38 | 7 25 640022 loop25 39 | 7 26 653232 loop26 40 | 7 27 208950 loop27 41 | 7 28 164716 loop28 42 | 7 29 641798 loop29 43 | 7 30 645894 loop30 44 | 7 31 614270 loop31 45 | 7 32 177882 loop32 46 | 7 33 594366 loop33 47 | 7 34 649578 loop34 48 | 7 35 641730 loop35 49 | 7 36 242246 loop36 50 | 7 37 205478 loop37 51 | 7 38 631000 loop38 52 | 7 39 653880 loop39 53 | 7 40 653894 loop40 54 | 7 41 183950 loop41 55 | 7 42 63614 loop42 56 | 7 43 157376 loop43 57 | -------------------------------------------------------------------------------- /proc/stat-2.4.21: -------------------------------------------------------------------------------- 1 | cpu 83497797 33759 24080131 1872241329 58643672 2895401 1573921 2 | cpu0 83497797 33759 24080131 1872241329 58643672 2895401 1573921 3 | page 1138148875 1022982406 4 | swap 143064392 145792094 5 | intr 696424900 2042966010 2 0 0 0 0 0 0 1 0 0 2463880312 0 0 484527294 18577 6 | disk_io: (3,0):(486355229,165458063,2268642596,320897166,2045955946) 7 | ctxt 3597668172 8 | btime 1096375576 9 | processes 31542089 10 | procs_running 1 11 | procs_blocked 1 12 | -------------------------------------------------------------------------------- /proc/stat-2.4.24: -------------------------------------------------------------------------------- 1 | cpu 421667 648 73528 27329820 2 | cpu0 421667 648 73528 27329820 3 | page 582900 3939237 4 | swap 46 142 5 | intr 29160226 27825663 2 0 0 0 0 2 0 0 0 897241 0 0 0 437314 4 6 | disk_io: (3,0):(440938,35718,1165792,405220,7878474) 7 | ctxt 2787728 8 | btime 1116449546 9 | processes 49298 10 | -------------------------------------------------------------------------------- /proc/stat-2.6.11: -------------------------------------------------------------------------------- 1 | cpu 2185800 2819 824286 21360661 305445 13332 0 0 2 | cpu0 2185800 2819 824286 21360661 305445 13332 0 0 3 | intr 274063051 246960995 390491 0 4 4 21162475 0 2 7 2135609 1 677108 1623104 0 985826 127425 4 | ctxt 327578142 5 | btime 1116552999 6 | processes 76650 7 | procs_running 1 8 | procs_blocked 0 9 | --------------------------------------------------------------------------------