From 2b4deca937d3ec7c9ef44be6b3fce81cf5c88912 Mon Sep 17 00:00:00 2001 From: Thomas Diener Date: Sat, 14 Jan 2012 22:13:30 +0100 Subject: [PATCH 001/123] snmp fortigate plugin: first draft --- plugins/other/snmp__fn | 127 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100755 plugins/other/snmp__fn diff --git a/plugins/other/snmp__fn b/plugins/other/snmp__fn new file mode 100755 index 00000000..dc869c0a --- /dev/null +++ b/plugins/other/snmp__fn @@ -0,0 +1,127 @@ +#!/bin/bash + +#set -x + +### Constants ------------------------------------------------------------------ +SNMPCLIENT=`basename $0 | sed 's/^snmp_//g' | cut -d "_" -f1` +SNMPGET="/usr/bin/snmpget -c $community -v 2c $SNMPCLIENT" + + +### Variables ------------------------------------------------------------------ +fnSysVersion="1.3.6.1.4.1.12356.1.3.0" +FGTcpu="1.3.6.1.4.1.12356.1.8.0" +fnSysVersion="1.3.6.1.4.1.12356.1.3.0" +fnSysMemUsage="1.3.6.1.4.1.12356.1.9.0" +fnSysSesCount="1.3.6.1.4.1.12356.1.10.0" +fnVPNSslStatsLoginUsers="1.3.6.1.4.1.12356.9.5.1.3.1" +fnVPNSslStatsActiveWebSessions="1.3.6.1.4.1.12356.9.5.1.5.1" +fnVPNSslStatsActiveTunnels="1.3.6.1.4.1.12356.9.5.1.7.1" +UNIT=`$SNMPGET $fnSysVersion | cut -d ":" -f4 | cut -d " " -f2 | cut -d "\"" -f2` +SCPU=`$SNMPGET $FGTcpu | cut -d ":" -f4 | cut -d " " -f2` +SMEM=`$SNMPGET $fnSysMemUsage | cut -d ":" -f4 | cut -d " " -f2` +SCNT=`$SNMPGET $fnSysSesCount | cut -d ":" -f4 | cut -d " " -f2` +USER=`$SNMPGET $fnVPNSslStatsLoginUsers | cut -d ":" -f4 | cut -d " " -f2` +WEBS=`$SNMPGET $fnVPNSslStatsActiveWebSessions | cut -d ":" -f4 | cut -d " " -f2` +ATUN=`$SNMPGET $fnVPNSslStatsActiveTunnels | cut -d ":" -f4 | cut -d " " -f2` + + +### Functions ------------------------------------------------------------------ + +autoconf() +{ + if [ $SCPU ]; then + echo yes, OID $FGTcpu can be readed. + else + echo no, one or multiple OID can not be readed. + exit 1 + fi + + if [ $SMEM ]; then + echo yes, OID $fnSysMemUsage can be readed. + else + echo no, one or multiple OID can not be readed. + exit 1 + fi + if [ $SCNT ]; then + echo yes, OID $fnSysSesCount can be read. + else + echo no, one or multiple OID can not be read. + exit 1 + fi +exit 0 +} + +config() +{ + echo "multigraph fn_cpu" + echo "host_name $SNMPCLIENT" + echo "graph_title $UNIT - CPU usage" + echo 'graph_category system' + echo 'graph_vlabel %' + echo 'graph_info This graph shows current CPU usage.' + echo 'graph_args --base 1000 -r --lower-limit 0 --upper-limit 100' + echo 'forticpu.label CPU' + echo 'forticpu.info CPU usage' + echo 'forticpu.draw AREA' + echo '' + + echo "multigraph fn_memory" + echo "host_name $SNMPCLIENT" + echo "graph_title $UNIT - Memory usage" + echo 'graph_category system' + echo 'graph_vlabel %' + echo 'graph_info This graph shows current memory usage.' + echo 'graph_args --base 1000 -r --lower-limit 0 --upper-limit 100' + echo 'fortimemory.label Memory' + echo 'fortimemory.info Memory usage' + echo 'fortimemory.draw AREA' + echo '' + echo "multigraph fn_sessions" + echo "host_name $SNMPCLIENT" + echo "graph_title $UNIT - Sessions" + echo 'graph_category Other' + echo 'graph_vlabel Active Sessions' + echo 'graph_info Active session count on the Fortigate firewall' + echo 'fortisessions.label Sessions' + echo 'fortisessions.info Active session count' + echo 'fortisessions.draw AREA' + echo '' + echo "multigraph fn_vpnsessions" + echo "host_name $SNMPCLIENT" + echo "graph_title $UNIT - SSLvpn Sessions" + echo 'graph_category Other' + echo 'graph_vlabel Sessions/Users' + echo 'graph_info Loged in users with SSLvpn (WebSession or Tunnel-Mode)' + echo 'fortiuser.label Users' + echo 'fortiuser.info Loged in SSLvpn users' + echo 'fortiwebs.label WebSessions' + echo 'fortiwebs.info Active SSLvpn WebSessions' + echo 'fortiatun.label ActiveTunnels' + echo 'fortiatun.info Active SSLvpn Tunnels' + exit 0 +} + +values() +{ +echo "multigraph fn_cpu" +echo "forticpu.value $SCPU" +echo "" +echo "multigraph fn_memory" +echo "fortimemory.value $SMEM" +echo "" +echo "multigraph fn_sessions" +echo "fortisessions.value $SCNT" +echo "" +echo "multigraph fn_vpnsessions" +echo "fortiuser.value $USER" +echo "fortiwebs.value $WEBS" +echo "fortiatun.value $ATUN" +} + +### Main ----------------------------------------------------------------------- + +if [ "$1" = "autoconf" ]; then autoconf +fi +if [ "$1" = "config" ]; then config +fi +values From 0e17b4698209d229fdb57f9d313621b73003affe Mon Sep 17 00:00:00 2001 From: Thomas Diener Date: Sat, 14 Jan 2012 22:35:09 +0100 Subject: [PATCH 002/123] documentation added --- plugins/other/snmp__fn | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/plugins/other/snmp__fn b/plugins/other/snmp__fn index dc869c0a..1cad6bc4 100755 --- a/plugins/other/snmp__fn +++ b/plugins/other/snmp__fn @@ -1,4 +1,38 @@ #!/bin/bash +# +# File: snmp__fn +# Description: SNMP plugin to monitor open sessions, sslvpn, CPU and Memory on a +# Fortigate firewall. +# +# Author: Thom Diener +# License: This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; version 2 dated +# June, 1991. +# +# Version: v1.00 30.10.2011 First draft of the fortigate plugin +# +# Parameters: config (required) +# autoconf (optional) +# +# Usage: place in /etc/munin/plugins/ (or link it there using ln -s) +# (Example: ln -s /usr/share/munin/plugins/snmp__fn \ +# /etc/munin/plugins/snmp_foo.example.com_fn) +# +# Global community string /etc/munin/plugin-conf.d/munin-node +# [snmp_*] +# env.community private +# timeout 45 # In case low latency or timeout +# +# Fortigate Activate snmp on your Fortigate firewall. Fortigate documentation +# at https://support.fortinet.com +# +# Tested with Fortinet Fortigate-50B, Firmware 3.00(MR6) on Ubuntu 10.04 LTS +# with Munin 1.4.4 installed. +# +#%# family=manual +# + #set -x From 932b38d08698510643590070cbe932debd393475 Mon Sep 17 00:00:00 2001 From: Thomas Diener Date: Sat, 14 Jan 2012 23:07:42 +0100 Subject: [PATCH 003/123] sample images --- images/snmp__fn-cpu.png | Bin 0 -> 16039 bytes images/snmp__fn-memory.png | Bin 0 -> 16135 bytes images/snmp__fn-sessions.png | Bin 0 -> 16407 bytes images/snmp__fn-vpnsessions.png | Bin 0 -> 18150 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 images/snmp__fn-cpu.png create mode 100644 images/snmp__fn-memory.png create mode 100644 images/snmp__fn-sessions.png create mode 100644 images/snmp__fn-vpnsessions.png diff --git a/images/snmp__fn-cpu.png b/images/snmp__fn-cpu.png new file mode 100644 index 0000000000000000000000000000000000000000..91cdc83516afa1632a11d10e018cf91c1b6e9187 GIT binary patch literal 16039 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 literal 0 HcmV?d00001 diff --git a/images/snmp__fn-memory.png b/images/snmp__fn-memory.png new file mode 100644 index 0000000000000000000000000000000000000000..ab57b1aaf687f421d9f2b43dac66354f27a236c2 GIT binary patch literal 16135 zcmbt*2{_bi8@E+Cis?*Gv|MNWeegC${;~MJHuI)_Qsi>%SUAZiCor-G1 z6#Pq1vlaf!u{^0+_+{&T`AafXtH?j^a$^Iis18wGk-2cgHgqh^&h|#b@~ZZ|nnOF= zo>Cp&bM^eQLnq(fi2BmJRoU1Kopv|Baf|Dz`=<_Fi{Bb;bt1B6J}%~#A^VmDXQPBe z7ddq4uAj$fyB`gHwoT!3+14vpPPa|amcKpB9~9)^R<1DZETI%0l10K1L`Y6(^Xj2c zv7zTz}mde&7V#c}xQX_U0|CTd!?BRo9b^jxU#X+~-p1`X~uXL%0D z$FZl7!>-)C`TC={!~j|Vn_ivs@uP=s#TzftDEbv9Ni*=fo8%a<=y3*}1A1=v-J{>y*>bz(?x@bOcp2BHJC?hjT5QAV{_&)!U$ zsTT9w#Vq)!!>UPS-f8kH_3V$9x*K^H-M{w@*F-72J^i>d*J0CX+sSUhI_|v;yA6$u zf`rg)%F4Ko)s!;wLM=SjS3SjUM6)-d{~_! zNKIJc5@twgJanQ(rKMs_kI>gRd0E7#is)G+>|YV2RznGrOWuWUv;o1vBd-R#R#$}D z(oOn`Jm`JPQa^qAX7UPl@Y_^hsmP;lDrE0VeOXbZ=$tXZ>({TB5eQs-e16JNa>=$+ z_b=roPGbgx^cQ}l5Sr6XnCmY1h7HSir9f zQ+=#hzTDYbLFVDw+UwzVgN$$hCEkpBF!Jj?f?cv6Yu%EMUzy|kR#8Fjp~Vcpst%c{ zE-SlR_rS2tR{YjJ+8;u5=4 z5!4qfuJ7Q?QXO`s==m;Y+tGxKOkzyP+cP>_Q}hYeq8OziljK{%mcx6Nmq^rkQ|8_I zj}zv1F`j+u>A5|>`5acA zJZC<5_QB;4tuQ;ZZFWXwJJ7^*H@R`-(~3zLPktaeUw z4Qd+p#AvNmlZESVXe5GzgPSwWIH4Kpz+NYWUvK&n7xs3rY`&5v_|h!mA2Xxs7W~Lw*CH>+8gTv z-KZyUGpPCTjtXe!EqIKSO}m*JVQ7~ylTrQ@fl}!-H#g@?kV~|)*w_61d%ov>?zd8y zyq@QMd>HbM5FzxjGiUt0c(dJUr6}LcD(iB4h@I0VNpEZ&a_`^2AL`7<%WLg8UmVig zdp{O;M~fO=-Gyzsu$jSLRP!Lm*|W7ogV85XHg4T{@UAEUzF4V#NY$VvB{65bH*aaY zuYwQX50%?IHm|B=h%JA2!ByCH@|{Xlt2x{Eo74je*_Om!fqf$Ovk&0`Mi+4Ct~^1` zA9+qo)8D0OMV{wt_9#jOi;&tag2Zfip+5MKeI5#TFpU!kuWqA}OC--VX%j?-LJOw{ zDq7Nv7;L(nGTKr*vaS01%L9A~0|Nu7QZ9wJ`Po*Z$D!v2>4%#!jgTyn1K>DU(Kgyp z%NJ55_u#?%z7)Pr0l$}--x$QPcs^Z^?_Zl@>l)m5P-nE8@4zoMBneypI0&t#PYu7g zKcmy$C?r_YX>oEOP&-?~8BXjM=r`ZKeLK$0ty2>ziz+oTG^C-W9fCeQ0D}#+TGTPH z3J3fZ%%P$Fp&=D*Z31-Lw-OH4A#b#v zr|0pLCwr$#+4W&!na+rr^=^(+OS0bCu;{vF$AN3g$~7a8)AhVVGq}7pWZ@lv7|h#DGDLjIy;UX|9fM_~Fg|!RGQSfuV`oUw(82|z!X%w3 zwp(f06s}+2%b;Z&(KZWEz*T#7u}MZw?yPCMaET9dDQvvBB+aasHsuKnq1LfI{k75Z z#D+MAs7rpwBqhV2KHZ#cGchi_QY+7w{IG^&>u#1ZWM^S{$Iq<7Ln1xqQQRf}`X@_LUddmD$*x2T!{g)Ww`JtUn&^G=IBvDrX`uggY_w$-;sybnmR` zZ`@g>LWp&bgsuj^5YN$pRSAra&s#!8hrLp|~EF ze*WUstD^T;0z!SEi-n0z-~wRJzQFv@a+55a?*V?(cB6t5~^ph!38VlPT>ufe-`VGk0x}mCiT|=WX%1?46 z@ZsfDS|1;u_}eMZaX4|)tbsT4iKuHG>W^oSH7B}G@>Pw-3azF-Q#Gd754%_R+^qa) z;5BviidkZv`YtKY?-B4=JDCN&rJ(isCiSIpeYHeysSUZCv%3IeIoA3*J6p80{+gzy zW{6U(k+3FUNt(U$KUxayhQfwyAI?K{zmJJ2F*tK_!CF`oL5DPSM~aGzlQFB#>BcP^ zT^bCJ^72-j*q`e~u;hz9Y;4s4;s#R*4W1@d56{XXAE^8jfnK<%&L?K}jA^Q_AKkG% z-{u&~cqkbDemFjpNe~@(PmObTJyx{1G*+8ScxahK9K7FQh#=PH@1epk=30$Hh0UMR zU-CtMJEqABe^YQleMg?<;$=#aKj#nAWUZNTHSMFOB|GgMY%`MKoA#g$4GqhxtF3X? zjfQDw^(qhA&kpyTb-FCuJKjk$%4oZC^=g4(+RIn3o_3g`Eim$eNw+R6e^l`$%*CRT z?TS5995@hky+d-9Vqs4juX=Stcq`^)I| z($llb^JTYny$sQ^_i&?S>F)Q#R2EY2o}7HVseQIqo^ABH&Ez}69N)>4+ue`7&}B@( zQuq@$koDy@O%uGg_G1}+xO~~AP{zhQfDU3ENaNltA}{+&{c;r9ulr<*ojrT@@|7!x z#l=Hkz1mlLRcL&E$+cBUYVNiQOzu63hbqqAks|AB{PAJC%B1+2l;AA5wYO_P7x)kt z*9X-Z35UY8&c)JJSh>Dl#-&Dq2E{HgP^$w z4<5Ym_rGxS=6*n3$!)WT$s6~17556nv(eK_tEwLG_xFcM`r^fl^A|2WZd*>Y?e*YV z7(-)5tyn*0m_`Tsb)6toRUH)+oR2r!c>7JwV!2cw(+iBAz@gR3hv!$aYHz5iExkV{ zKeOYSs0^p1!@M!fvs*aZGfEP3``mW3-meNiXG+Q`Xp+-_U3^%g?UR|I6e}dud-z;v z=XCk(8+>d`%ome3!N|zS<~%1cZj_YfxG7%hgJf<&2w(r;;8q#>vM=6^N-K;_E)@Q# zKenlaK2I`??WO-h%lWm6h+5#@+XblM%9SgY#A!wEj^^ev;7ytHtwv$B&mJ6MQ~1DT z`1d_Qo+5m5j%X>{8>@T-2wB1L70)6-VCAq>HD`v3T(`~1_!ymE399+~PaX=hVWU5$ zaCfBsqe7VI>uvPMGBwTj@ADA5_UET`G2EIGKS4vS+lT|Rp2QV%ba6??H9)2}X zi8wPoT~<{!cS0iHmsQM>xAX(=i@CLJEXVuyYNJrQ1w*RP6`xTdf(Z?NV*5O>a=)IE ze=j<&#uWZ^MS$zUzn^DC&VRvRt<+CnbvTvt>2mv_5f7%nt=!rH$15LkW|Bqvg?F#t zWlDgOq zpvbDEX(ePVEd}6>5nsOSbpg=J!NK8zZp4~&cD;N)ZT_jY&LIB7J5hf6{5X>&4|=Do zw5LCR{vJEq5EKMhe^J9%S@X~+*J?kOGLwW_391YFsO@x{@g|Yz7#61Zj<~0gc&Y_X z=#B$CfUGwa6&1aB{aW_HgCj?e9tEO!?b@~O>qDcD?B^O2W`l)mVzUPZ2lp~FJ2akq z%v14jxgTq`)=7TWo`yl|D7PMK3>~!B-tMoimBHt{#KtobpmO?W4YFAD{whD-xnO#R zi!}FMrL?%`+$m0wuJTalIe_$181I`*^T>LG1T5`&L&KB6069;d^aOn)<$m#Y{OgY* zhkq~jpLH-L#CmM6kQWD{WXRR1S6`Ql>PxquVq6-@iYSZAsR9hf7ZOR}2v} z8%t{vjjjv*kMav2u8XAwa#Y{Ypsc1=s;2EEi4=+~I$!Rl^H^P;z~~ceqhT?g z?`Hi5v||CdvOAQih)597v$If$T9{=k=&l-r5trAVEcJfKw!!MK3VwpPw3*o%g80l% z1n*PHzu`FyEQJ%s``fo=fE<@qW9%bK&i(B4e>K6rT~)i%g}BqL#JJDVoCe;tDQ~%2 zOF>~LQh`4@b3-O49|3c^eEIUEdQhHo?#9!zH@9&HpMRs9>O4)zMEcL4Cpmn$HhKq; zuBXqQDc`)g<$Q+ec*qDm7aqi&${>E~xQ1^C4h21f_Png@7CJh*d?8wDz-H;Sg0BKq zUWcP}WS=SVxh8dHHw*<<4<)D%sVt6EDwtbXIQ`aD$!c7ymBh;Wq`Y`<7_E8L)g?UA zZo74X&wDfSKZk|-0y+&yFav^4Zg1)F{V(MgIwqV;0hE?iRz3hY_@gbu@9o=zKquY3 zy`jxK1uVUN`*v7Vk+nh)2{DSziGwhIGz933E8{s6Uo45&eOW{;IRfv#^mR5BsJLX6F2B9bGZX+6g;yCTk zVd2R!wMSY)dA(|fkr?&!;86B^G}YI?2n$O{t0M@m+grK*3#OsE+?yI2?xnN<=hX)rZ4kQq_`o1^TuRG2KfLiaQsB@+M~|-z^!ML|*PlFDzymTX7l?Y}9XWNm z4mNVWEajj~b;LA1S~PHEMXhaqG_?inxmFBeT}a36gMVHpwLGg5ugVRg7xf`2DJ;wr zUOdG-*0QRQXb2Dab0ce(illc|ZTmag_z4O{2bPMKAOC86$jjDke9Vq^SX(#uqEiRF z>V{U9mn??AAO2x2R$gBIqa&v&+1mcbjaL=ozm{E!a|#HRNct&K+Iwl)-kUdX`eI@4 zW`M))b2%#i2B}1-$jb@}51`7OxmDKRBUX;R=<t_kNGn zZfdDR=gz%`?{U!OXT7XFsc_sd(K5ieD=%zu(f-n=-KTZG?W?V={g9Aw^wcRIkeHVe z#MfW+VC-cyx$XeW96X4icHNJuGeKfvS=!AmsbFq?c4>ahLOe|JLD`-_`=59wQp7BP zGJ{>UG|b-+85Pxnv*QPS!jUpnYRdeGbKM3j_mG=u;rmq`jE?h56HBioK^g_9kJT2%>y+-sR-E~?;{l9s1g0lNEd&MxxK`;! zefU6=q7WkJW0aV+)vNeoQ=(?pk1rMbJRDqqp06pjBP&47#TtbePv5120e2YqJE-Gg z(JDtw_pZNG))SpofGil#8SZmhng>qkE8#E?lkhS{z!NzL2aC1+7honee3Fm^neWV* zGpMC~9w9R8o~a^uVKL~0q0V^=y*o09`~SqCAd?<@Xl&eH&V}u#%n)L2COU8N9H4?T z4&WP@`OB9tKevtjud|9-B*xl-czZj?`V=Bf@bK((5wRFN4Px;DKCMa>B?;Mk_YM<@ z#NRvJXT2(>aL4H@&=LC{=5P2L3j-S5n{l>bJ$#`gAk=t@c`QATtMf3^6f)d^4gu-{ z6~Pf42r5ub_t4R801_Py&HZ)ypXAHYp3xD3dCo*ie!)d-t(v|bz_HQaNEZPwO4di| z$2iaF>fVTsow2eO)DxMBNFQXkv2^d51$Z%2Dz)Mdr#m1hs2r4eC1+M6E*7r3HGCvD5JBCNv56OKf&e0U9y7 z)YsogH))f*absUw23o(^b3bga#l>~7fyaNiMmFv}mY8?3bw7_`A@TO@+ZvC)Zb@jo zDiyFBs366?n^-WwZG8Q1a+Hu#O3xQ>kgmaNKNErTo@4TLcJ8_J9oC9*U}1C zYx_a!BAsy3(qaRECK*i)8k^hi9gW`LJr+Pu| zUz7ZM3ZCg_SWDhdz7^^moZL1B9N}Kt9YiWI%~(oMSaJpJ#2oL(33 zWBStR@RaEagbRUn(NDs!N?<5`>~>%SoZ#W<=aM2{IW#)#|LN07Xer>j41rSkpgL6K zzf1K)b>-$s!#1N{8EoyQojg3VC!DPGjwL52O9txPU?>C40zd^NE&wxVsW+mgttxlZ zUQ}COpZ;O{?$`{+7bXMWUV~!=PMBwd;XraJTi-YCq@A|9xe+lj&q1ENrlo}fcne1s zv{pd27eR#FvSkaeRo?DcHFU>Sq)U8KM+PLHzfk112e;*b273P{91LBp(<&sIGEc#R zPXWf;d*Hy}+fC?)54(MYI`6_ye~=;VS4a;iM}Kal!MGC_R9UHjc#;YV-a04>DBB=C zmPD&stP%FTmKMEW-vVS785@ONw&}29fH5eRlQ!c&7+ZS@&=CM7J76kQmlYLtfz*L} zinz3@@wa+1&YoLWHz+YVl4m`3hs|kLxjUq1_HGC@N$BU$I6BNRbvs2*`PQw2uV}w) z*|dJ?)wB}-5K%5LczC5NE7kuU=_4$Hq8N)V8XXz=++zId&6|y#_?>x2L|bEL!7)S_ zG^Lh2=(b~1#Jc0s{e0BF7@eGlQ7aV{+h-&Tu%_Ok)*({i|B1l0 zKqv-6NJi)eh=i>E;B6JQ03*3=xBnwNMU6Ly89m}d2l4Blo${VIrTV`H7KjIucuPri zhv?eCdI^5v{~B0gxX2u7nO^v5F&{sc8)$<}2=nQIuI`Dwpct5`DjzX@0_lQlH*XFc zltXu-g3dlmO%(uJ{))W3&UAnIe@y{|T6FFOo$+B2?Z0rE=r5dhxgBP6Hx+~skP{DR zTykYWCu3J8-TG~#u7|@c>NxVf?(LK9J^XzA75z&pJHGcn6bDQlk$DOqE<>5QOHXD$p5lv0AW5-MuOVnSI14SACdG4OKrdXqam`>@=z@@fU-*&Ou*x6Gs zGX9(yHGlGv*IN4VdxkvzdzwF~Kt3>G-Y0>vVufwV>12g>3^6^%ni-|W>C99GaS@r} zo=I|lsY2K#7=yROBu<|Erptz$Y2>pyHy}RjL@tR9 zdP+|qo^lk7;;nc8Cbi$rw*1)jZRdRW2m9#~P^#otwrH&>-%q-vV3)pqREdRb%O+^_ z4}fg%WS0yDO?}h}8d=Q+z|R-J8wS1x-P(8VujLBAu5iiMo7 z1KltEX1QlYwB^w(C@@lf^t#j!4n}?nX#Oi5) zGTpD;y(W+xT3D_NwRKO19vW`jyA725gJRY^A3uK70DJqt7<_~q$JFk8=r(g{*xeO0K6hwC_fH1|5p(M9$ZKwI#Nq`S(+Q6Q9{C96L5w znq-rOPZ(X4dr{|;L1X_?Q2Z3d{Se|}4mDU>_1wlosJ zucS9SDBX~->jORLPg(Bk1eW8s*Vs^XI*~NNP$WY7#lyn`JPfM4e|L&V>OueUwBkjX zU_stWeMrYC=iLpQ_&#}hVBfxdr}e8@fXjRjEY!bbTxA#sM`0!u2jvjq>NX{u$P$!-;EjUC3%*NE$9TqB z*Iceo;W#t!8DC-^uQ@x9yGcs^w(h_dK79IQ?ida^2P6%HD~|vC`Niz4W#AoT;?mhe zXUj+Yz?UyC+S&@AIB~+bLd*n|&bVhBt*RU@%UGbhGFDWCfqe};mw1a>@ z)TBS*aTv)VX6(1oABb5D&tx(?k8lS%PcS#2me!kIqY4(ie@MOmdz`o;y%>eko9N7i zR8@GBHYN;eI?!PMS|!pM`!D32IH8eZd&UVR^%avoyw88(l9xtO%4rZSy^nt*IJJ3 zo7=L5qHQNaTNQex>4NJ+Vf*^N^p7#mKeI{TP+eBv5BXx|_B+Y>&Wbt})$(Uv-FN zhkg|#py<~NKj6~^gmPzJ63A~O;8}ly{g${?$a{Gy+1W@)H1T>LUakD&|4-a@ON5T^ z&?)gYm^cYG+#bmYIStzfkOkCmCJ)R`bYbQBa$i}EHrmyL=9p3X57E(NoD`_~^w>(? zD$fD0zSyG^c}u)gQ&Y(Mii%`Fp}1pUwd!$K+9z|ejNm-a@4PaD)yZgspp6Yc-KdY0 z_EQCYRlVYRsT=}Rq-C|6H)TOAiCacK2cdj`m6yhI{6MQry&AQow3(UJKAEA1oi}&( zIxI+5uO~Nx`apHH6zycGL1?#O!%0*8vUO~q{z{=`2900N@&T`6f48JNbc+j1D@GqJ zY%29q%peerm^d96j^o6A6^YwdKRYkmgS~`AJcqCtNzC%7c9<`G9VB9|Xo2N8(fyIQaPkz{Z>! zj`9=rh4ihwH=9JT^YS9{4M4{hA8f+W&~~RH-*ctb^LtSjNn8C#ONtxF)X%BeY+Xi^ z@Ocep@I9L$PAop1f?f6ojswACS!-*-UAuPq>Qn^sae<@{vhEkt4$)u~-UT6&?r)zR<8`5swW)iQJ8_d4CP|903yh8s~&TS<;bCR}6%i$Cf z(b5^8C}z;MWC@YNNT%pjZM@rwv-4es0HP2iT%uxPVj5n={9FSOE@GVOQ%TtTJCd(E zDGoqpX(bm9Q3-Y0M)fV!L|tMOc=!U=BX#(zAd+b+`tE9i1g6}h*0L!y%JuF@NdNFZ zL;8k@cJ97M)@Lnw6$z^u=~~xlB=T%egb$Nhk3SrY;9@U^wJiBpSL&IiuGPHUxFdJ& zLQcKs83Wa=TzjiHxwbO-^G{bH2~~1G87#Dp&YN`l-9$9kK4c+Z1_xh-tb~}&ct3>1 zXdwV;G;z{{-*$Pf$-vOC)*O2Q39}$xX^5bS;lw8zhNp{NzQ_-UCGvXBt#d*Rb8PqmHLV#pZQXenFg0(J9m7g- zLl8!ak!EU9xTI=o|eN8^Vm(T=k@*hE?lK_3P01u}d^q@cFlW9Z8Bnllkt`%)$Rf>M8}Hma zW{u=wAgcg@t877iIvWh^%k*|A$zITV1Ox>^6D)!JzYAJU{+MEL5f|36U}37SEpEhq zgXd&;n`w@Ztk(f$_~P)QTiKQ;5J-l&ydWO|I=@4#ccJF1+LbAsW89kR0UH>L+9Te(v1jUmH^4nqa@%FKUHJ8|RLH0+k2SCKTZ7O^b z{&uh`q!hWPfU}zcug+vB_!Aw27BY~Kw1EwNNs~9Tdn0(NRDKfk2SEu5h44Gr3bwBU z2j@0ksvtj7v*;TaD8L|J3Z#IIr>#bA#JaVOXOHqqF8$yyy(W4d+;KkKgS~y+NP5f#L!S&($PE-@ zhpr_yH(GX63;g76o2J+xeXLVs+~^Dl+hLiR!bshnYtoPg@27jo>OfZ{KCC;2Q-i(F ze?e48ij8K@%213Gv((mg3*0amFo@@pGoD>x7H_A^i5{FnB-zC5k&_S#D7dMnIk1qI zm9tzR)|xH3I!I-QqHI_*bKAtFw9$lNn_xb*>004Pmmh!EkUH-FO))Rik{ z@hTKBC}$w#9yV6@;}IYR4o=P>7$ix#^GuV9N^?+pEW(dmU^a+}-X8xDAAf{{;|ZM8 z`)PMRf+-Fg%BI#d1d(vWgXo9Uu)ms}IOOEpF{{ha&r%?_Sb8=fPfNeYd&T-m$Ths= z^8*fW^I+Vq8f~+>f1Ax{{rqT?-{RsTn%l}gtaY$Y-xP?3ys|Q#g#Bzc=)OCD-mkb6 zuAkVV32F7G+vpdn(9Y73#sa?qMdvo@3q5a|fLRHM#X7ed%x-#@p8f{#He8G87}2?Z ze*unJmCJ@r$4{I%eE9HVxFwLW^b8$p=LF+GT1O{Jg$eCem;v{=kTr+#6gKZ*5hiOi zn?c|Hw0CaXr?fi0yoLLXN!rML7lc2`P>ml1urS(`1j)?j0JYLaTtc(-Y~V8>M=yxn zv4XMsj_DCQa$5kRdPsyhQ8VZIjT`-zxa{m;h^Id`>nlkMBEapJD!^++u%fmwY=Q&~ zu7@l=v&qbn1gpa)xNat91LW5@xVYRP0xz1Ixe14_?o~&=n&s0``h7kN&?8~tarxkY z7BKmyemoWr@yYE($>z0E9{sv*rC=`dO!Jljtv^Q zbO-s_+v&zZI{2JXo>sYEN3+DOuI*5B_+1mQz~KRpN=bFax;esiKKHvt^{vFOjh7ve zY_B)7kS}sgA~%i${%*ks1<8*OR-*8G8Z?dPTGr&^0*vs(GHw?t3OuWyBfsUQv>e>3 zUT7~kngV$w2uON280xlXn&IXe)t!?FEgGerDxIB)g{y-hXmhCeIta8Qy#em&ZMW4L zT2*LIkQh%&t6H|w>a4E|arAzNKpRRCNQ{|qpF`Mj;WXeZsj{-NNe4!Ki(O0U)APEl zE3ls~aMdaarW7>%p<4mCgtTv(HI1!(E4SL(wTA|HF*b7+fqrxdLc+y}d>U>_Vh3T) zS8jwBv#<%Q^x3htrW4Z-v*wP~itsDSFGG3^$t@r$SqS7&ArP{HH^+O!rme!uI@W4R zHgjgAm91)4hF;LD@29jZ;8&NV;Cf3w`^qG>^xV|z^l8jr@Z=n&_k?y+k2W+&_*uwq zgI8$LTeJ=0)U6tE?G5CNwE4`Ov?{0aQuwx`OvLf&Gf$AamQ4be4uHtNRL$1r+dnq>p=i+6}E5FK#<|2m%XW)U;n)$xAHkQn-DaQHory zl)1XH#L8`ueJ2h_W~nafXzepeFd3JZToL7qGJ2mgU8=V8)I7)2;pR|sxT~I)-N1g; zggeEM9YAib;fA^kX{A7@4CBL7Uc%)dWF7;VZmq0P$$}e4v!hLfsm`Y_U+(>dE~KCD zjKJYS0ny*fXk(pFJ}CU?5HJ|iQDUOxcxTt$=it|#h6`!HEgTd5xR{ujLYa2hkRcCx z^5n_k#)J?h-LRnQGEoZFX`T__b}t+>Jpg+!mQ5;~8}AI9n$jCH<2MRf8pVk>)V-cC zA@}XNkY=5|heN;?5Y+87!~K{1*apTjy1-G|<^ocz@7!opip_);(yu||ANw9^Q7xi{ z&#N~~`(ihv2WAqui#~P9gQTOl<@q+>cYV;ahu{OOahJZTNOUxmHj#uf`vAxthR_y7 zApIdu3-<^k7Bh7q6BR0K_5^Sczvb|?r0R^U4MMI-qmxORId72rnImyY(;;1ylA7~& zcj71t;t3L_)+QF1u)ri(`ifGTu=~jjho4S_ie%ind9xUn`p3a+u)HxXCrv>6KPYN> z93=Voea+y@S3qY52sF2#Gz|_hC&VjxHM4~|$a-+MgvBDK%AyKEF{Cbx6a;+l0wDEG z9Up;JM^N6(5JA5&{&sz1qiA&oxgF>8Dpnz=0FsBGLO&>Sr^A6A4oArs+BzT{X^2bP zK|&^1R9pL@T~!E1=xpU`{w7+gNeFv(B~JkO@lE>%zFH;71QV&%cy25faxp+a?ckOj zGTB)y7OSkpU`~<8ZSZY|CKl+KsU|zSPx=*Iai2c557wpVD`7|9_gEfo4TAf)$mQWg z$dSN|zX-RAfo#u+(4qyH1&yg5@{d7$h1`T+DFzs5G`!G~U-lwp4=W>}E;<>I$O9-8 zNBg-E-OCduQ4@;Ua7=3rgd3asF1qIfb9X;<7h>h$ut9LMClxPZZc1JP5gu63AY^r+ z@t5|U(f*K_7!w~~IXRW`Eb`G|4PayLhYrCUE-5Kdh3gg&T}9}}9f^;l#J5~|lxg`q zpjrndd7sko0A!=t2v;2C{n#Wz58z$UZ2QFE^40$R`x9!hV0R_tvGu{5JiWX`pgz0B zw|9?0m%R(uuVwb^0l*FUPO$ncTBRHp9)S)xI29+{S!>?BT2vFsJF2W-mnz0 z7F==PBdr8r6DT6mw|~FK{SS)x6uBWz1o#e5o@S$o^!%Ppn)NQodn3wMsVxW2t?J*| Tv;sHVsjkSPWD+mlee!<*^Gp1s literal 0 HcmV?d00001 diff --git a/images/snmp__fn-sessions.png b/images/snmp__fn-sessions.png new file mode 100644 index 0000000000000000000000000000000000000000..24e56dd2a7ca8ad8b6d081dee9ff61540a451e0c GIT binary patch literal 16407 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�xl8cj=dp z{LodN?thK3q`0v8sZFWqROw$YMzv+sd~aJE(!zORKW+QQozBOoJN>87IuZYx5w0?s z5a|wz1loXV>R6c753J3#5DT-W{9US@jUFQMkZc2>OkDI{*ws+P?B-N>mNfagSYO&8 z&_+IBqc5rg4$7aNmpW4awlI@yYB+=6gkk_ZEL2MFZe{>nru@vW+al=n2NAShj#lI8 z_5;@bKP?11OMWZOVFHWY{8#?Y@?Vt4DN3>w*%Q%W!66B-!5}F~J9_LGi8B?^ z%2$~TYbv$5d7Y}Co&4}H_+5)U+eIFEumOVE1mO^5GFdrRygrdc`Q?K^CO6NXD~zoz z&ep%aq<8wHP?y2A=5gf`7?dND7x-poW@i!CN_2FzYlP+oDzC;ZcGpsQ*6rdy+vA^j zEkU!Vap&W2$irPC>}9}*Z2Eq-63jwl$db6{u5$>>!Svmz71tzst1pyuA0^0 zvBO5DJk&EMdJ7qk?XYeM{@$sVoPb%K!W1TXxg|#k)YlKrwP-Ur_uW%k+s`3;IfR}= zZ|qO(wP6LwA;;qzcn03??GUq47-B&GNbpj)lr17d2_}Un2yFY!VScnph0Bl}#}Zf7 z4BhMFw7rf7XFyG@fSl!HPq~M*zy0O_1Mc#Q3MoL#Ai3oE#7nynBee~(alT+ojh6Q* zQX)lpuZe~A_v?R0xX6naFLaO!_AmL2YbNu14_Rlbc4v2RNdLRfVWTphqHDhMd>NLV zKQz3=)G~)FX~FsdFdB4&+W^`-eJLbP8*2I8t^8JqTbCICoTCI3|DP35g%4#UaRr|R zR`S=cx$=G&1~JyhgKwj7Ax^iPMX zN$vBNl+gG&9g`DO8$))H^R`ueTY2MCG6VBdNTzS_U-GMV4TD7@Y~>4@bg zIWemah-wGl0oaTiJWpaR361Av8%}>SR^VG@QpHfywD`1A=YvV`HAY{ba1RU&OoKJ~ z$+Kt9RYEK4I}KkO2rL|xRRnnnKcI{vTJKj)0jJ zaT%vhoyavjTkWu9hu-O?~C2#Se z1_-=5rzd#{%vLWX>^MQ+Pt(m0k23K5nr8=w8RyaOfuO@r7LD`6GIWf+n0J#=n#eLI zPo__+%C4<^bYC}GR)1{95%GQY-DidftCBLo}9wsB}o0zl{+Iy#mF39wur z;McosYs(Lgk;K&*VSjByAF;y1LRW%_&co{H-#qKg12M$WDJ&!Or9E8oam%yAv%r^s z$SL%o`;?l;dxCc3!5y*}1R2vPFwcD{^fao(z$+`v> zj%g=|E{HPUe_Q=GU18`;{)kds6w_VTUYu-vsi$K|`Fush*xmp>lRen!0$IHrJ54T_8(# z;fJ7h9s|D>bbqbo#3X#Bi1+ef6f*UP4}Q-}$57W1|B@dFk%GT}M4?4`5#m$? z3D~vGi)AQ#5%UL;s%UR72*o`QKMd%67}m3`H;6Z(H;~03<@MX$$RZy_MJa+?hl-elW_~5n{!6w z6R=7FU)MaG5(HrP5ASdSl zdRkeW!oy#8?%H)!Qt|~TzF-JB4+0QK0V}eyp^ela@Uvo}E`S&Q8q7t($^>%w_5$a? zfjok3e`;pwYw-8EFc;bS9hI#G?`kx-J;9X$Z@Ld61P#){pn}3q!6a;cSNwMSp4Ubw zmISk|BA2>2DJBSfP_%4jiEtZW33vem$44+a1!inNS_y3uodnuxB%gMx$p{@%2!fEM z0PO+g&U!IL07NK|lR)MR02ZnaAiMgpB?P(=cu7Big#Zh-P-L_K1KBOpHGG-ij=2m% z$%!*(T;83jUu@xz9IhbMhp|}%NFlzGc(?AIAK^c@-W1q?;(?qz$kV?{d$Yj)2n@av z(;tj`8&evoYlW@DYQ-hf<-3n_b0fosQp%(ZVFA&@5yt||SHL}Aj*Qv}+S=G4{$$8n z26Zj1&+v{0!~zY|Ui}BhyaR%3M~hr}fV>4)865$CHQ3g(?fc5Xce!*JN$PS%wIi`9 zJ2$AwUf-)Y_Dan;C;+dI^tK*0P;0cD6C@(32nKy#a8z>2c_MjMz)^6_d1no!d5*(< zJUu^7x)409#ANi^Nsz%;=aSLfGBN~PN(&}}KV!fSLt$H6nLh>}*R{h%ZWz0(tEw3H z?JMVS>-+@f=y=P_6|*IBHJ4L=m;_7*%Kd1z!Z{uF^b!V4#R&Coaw#EUU5{;xLQhNC z8p#eB^ni67D&D6a>bl#@p)#`;A?fasoSEa3X2jY(ob>J!+0_Su)r0?J6%O=doiX7Z zqh(HI*F*=cfF|AVtkwYT+e)~MB;c{F+xKLxb(c62RAtHAfF~`uug-@dIt2rI^$`Rt zGR6RPispsj?JaNwgdd7Ga?AlNgv~#mLO+tJip3-3Hz%f@^gE>K|MgpE_N*p z40l`?T*(Lr#*L&OWE&h9sRakA&7s6HGld^A8>=?J#dU*A5ovrFD#Z|tZA)uYaA;wD zjlwGHE(rCi)4AzdUQQ(3?qVfu-TwU=gL|sYWqwe*Koi$R2B;9-h(#I%%8u~de60_O z3xPu5rGYWW^mGLS49C<^i0K%t9qu4`$6FeM$_mpC#Lo_t@QRXB>iaUVqymy?e|~QN z=RcB?94qJ~;NZ#Ust^{NkcX&k+qV5!hmRV{SXYmTtiqQDg@h!+%w!L{6S}>bABxZT>>{%4Z4j-)A8j`XX7wZ@o!rkU>dF;xhCt`h9PC_mt zagJosXvDRd_jslT~+#CL#2_UV%+ zfJSM*)Yck99RjOdi@n$2(y8jb`}bEuMY8z*$4=-4iJ)QbKXBks2D838rCf-cn;W<* zxEasFAom0xA9AQ_@ffIYh;s*8&)@vvVMI6WVNWTtUTGwqf7+SH@uGJ+|(?f!k-lqF0W+#!VvNO^?I zN=n9K&2POaqB?kvv?!f&X;7zbF z{`XH?pd!%d63UU!T_SZv zQz3Lwd9|ZZ5Hy`zs0l3>=aG9%C{GQ+?Kvd+5bz5Ylo@Eo$O>pb!q0d(W>ae%A!n+4 zy&KGrL$BE&Kfs-sS)iEPxKKmQ(@95;0ETj*6OysMF<|m-zIn%_xl}NzJO>cXM!!+f z7k}>-m4?`aAijJTi+dcJu1RPXCeOnGzFN@b`rAK_3vH#PAAo}wJTfw|$eEf?v|K3a z;w>0fBPT-uMc6hKI1ayu{G~yLt3VPv^}ru9^ftdY>fjoO<8ZOf)U5rmlg-FMAMIg0 z7Vm_YQ-paC%H4?S2Q7Yj)ai-`bc>-R(6`chqj( zC!u54ZC;#8DmWC;NO3gP_crTrphcw4z9S_L@R8Qpg*$|d&aI!2x3~TC?e8q$?S##@ zC%^r23K9$4`}uS?6c*W1RwpL9UQXchZk`4v4rVJsPdH`-z@7&BrVN;Wj=*WM8ZO5~ z$uOc&5yl_)g8jDh#uAwoNrYl_Kx%;}!?wiS8jf^s+o?6Sbcrc*4o~e42NxTs#6``f zqNRqsetP7#Rn#BfLQdpB^(VOI&?i#pGI`3Y}&KoGu-cnSv0C2_8eN&;YK2 z7sV>H=>%sYMM9%h(!X)-h+bH=W@ra3W$}SMv-Z^8>vwT3^I@&~@)y^`T4UWl(Bt?> zlEKxH8<)*k>OW-*2TcM}dJd*{F2LYY#I9QwW(+5x2$1Dz!#m!+?E$0nK6Z9%U7Qd& zY#`XL>*>KR*qk_V;_xOyBls{Pr`Ny=<8Y|T*qORrM57e$ncRF4_)_Gj@)Vy$%7tbK zc0C;J7D)r`p7z|z|E*=?S-pGf<5e1qn>rhx7S9ipIRnp8Zp0G`GzdAUQ&Pfi?_GA+ zDx>i8TV1Iw0G{6au6bU)diCp1rR3=jI)*@O)@0K;ocz|Gz)t^GO5a*o9f2J$ISW+Mryx&uud}21+Kh`&l>^A!OBh`T;@TrdmxlqeLKaA#7FDvYNwLO8=oW zOw`4|5s%ojvTj#wzpA7JC$Lj$Z6p(cV9@zh7~|t*s?pSC)%$x`iLL7$ALEr8&NrLE zXLoSDhPSZGd%uA4+i(V#<6kOL%>l=}Y6fp=FYZ24@&FEid;1~Wg+uxx^v_i5%r9|# zR4=$9@VF>}(j{bz+6wT@4RD6>t$K22x1sKd-7Mwuu=CS2ARdGBv7Om}!+`;wgO9eC zG7^2n9KZsUQMT@xk6a^U;~IePfG0Rs88N}pLB!ny-Oa$k8=iXRQoHIkise!84gyxD z(ahW;S!}qYPao)2-&O!A_5_=J#=U#5n|L8JM>yOQhW2rIUK7=oYUi)yG{ihR#Ecgs zv7B$_Kzv9*V)tkw;CZia_`goK|5UXQ%DZi%voNP5B_*Y%ek)Y99eoth?SWwDu|yM|04M!XS1zes%sBu1z5fT_2J9mM literal 0 HcmV?d00001 diff --git a/images/snmp__fn-vpnsessions.png b/images/snmp__fn-vpnsessions.png new file mode 100644 index 0000000000000000000000000000000000000000..e98472760388cedd45c7b50363653206adff0812 GIT binary patch literal 18150 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 literal 0 HcmV?d00001 From 994980d233ff137edb58f029c7b91894bc76e90d Mon Sep 17 00:00:00 2001 From: Fyodor Yarochkin Date: Mon, 16 Jan 2012 23:31:27 +0800 Subject: [PATCH 004/123] Small configuration change --- plugins/other/riak_memory | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/other/riak_memory b/plugins/other/riak_memory index 72007c29..e35b1f68 100755 --- a/plugins/other/riak_memory +++ b/plugins/other/riak_memory @@ -31,9 +31,7 @@ def doConfig(): for name in names: print name + ".label " + name - print name + ".min 0" print name + ".type GAUGE" - print name + ".max 500000" print name + "draw LINE1" From 0eedb04aac5cced2d4d045a17c9c93e1a007c437 Mon Sep 17 00:00:00 2001 From: Fyodor Yarochkin Date: Mon, 16 Jan 2012 23:47:19 +0800 Subject: [PATCH 005/123] Added some documentation and handling of RIAK_URL environment variable. --- plugins/other/riak_memory | 13 ++++++++++++- plugins/other/riak_node | 13 ++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/plugins/other/riak_memory b/plugins/other/riak_memory index e35b1f68..25fe4764 100755 --- a/plugins/other/riak_memory +++ b/plugins/other/riak_memory @@ -1,8 +1,19 @@ #!/usr/bin/python +# This is monitoring plugin for riak Developer's website: http://wiki.basho.com/Riak.html +# sample config in /etc/munin/plugin-conf.d/riak +# +# [riak_*] +# RIAK_URL=http://127.0.0.1:8091/stats +# any questions to fygrave at o0o dot nu +# +# This plugin monitors memory allocation on each node. +# + import urllib2 import sys +import os try: import json @@ -13,7 +24,7 @@ except ImportError: names = ["memory_total","memory_processes","memory_processes_used","memory_system","memory_atom","memory_atom_used","memory_binary","memory_code","memory_ets"] def getServerStatus(): - raw = urllib2.urlopen( "http://127.0.0.1:8091/stats" ).read() + raw = urllib2.urlopen( os.environ.get('RIAK_URL', "http://127.0.0.1:8097/stats") ).read() return json.loads( raw ) diff --git a/plugins/other/riak_node b/plugins/other/riak_node index 948a4eec..0ac851f8 100755 --- a/plugins/other/riak_node +++ b/plugins/other/riak_node @@ -1,8 +1,19 @@ #!/usr/bin/python +# This is monitoring plugin for riak Developer's website: http://wiki.basho.com/Riak.html +# sample config in /etc/munin/plugin-conf.d/riak +# +# [riak_*] +# RIAK_URL=http://127.0.0.1:8091/stats +# any questions to fygrave at o0o dot nu +# +# This plugin monitors put/get rate at each node. +# + import urllib2 import sys +import os try: import json @@ -13,7 +24,7 @@ except ImportError: names = ["node_gets_total", "node_puts_total"] def getServerStatus(): - raw = urllib2.urlopen( "http://127.0.0.1:8091/stats" ).read() + raw = urllib2.urlopen( os.environ.get('RIAK_URL', "http://127.0.0.1:8097/stats") ).read() return json.loads( raw ) From d33f5b319f5d0c05224ef05ca5423a1aad505beb Mon Sep 17 00:00:00 2001 From: Lasse Karstensen Date: Mon, 16 Jan 2012 18:19:35 +0100 Subject: [PATCH 006/123] Add munin-node-from-hell, a quite unfriendly munin node --- tools/munin-node-from-hell/MIT-LICENSE | 17 + tools/munin-node-from-hell/README.rst | 43 +++ .../munin-node-from-hell/example-config.conf | 68 ++++ .../munin-node-from-hell/muninnode-from-hell | 290 ++++++++++++++++++ tools/munin-node-from-hell/notifications.conf | 19 ++ tools/munin-node-from-hell/simple.conf | 16 + tools/munin-node-from-hell/tarpit.conf | 16 + 7 files changed, 469 insertions(+) create mode 100644 tools/munin-node-from-hell/MIT-LICENSE create mode 100644 tools/munin-node-from-hell/README.rst create mode 100644 tools/munin-node-from-hell/example-config.conf create mode 100755 tools/munin-node-from-hell/muninnode-from-hell create mode 100644 tools/munin-node-from-hell/notifications.conf create mode 100644 tools/munin-node-from-hell/simple.conf create mode 100644 tools/munin-node-from-hell/tarpit.conf diff --git a/tools/munin-node-from-hell/MIT-LICENSE b/tools/munin-node-from-hell/MIT-LICENSE new file mode 100644 index 00000000..89de3547 --- /dev/null +++ b/tools/munin-node-from-hell/MIT-LICENSE @@ -0,0 +1,17 @@ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/tools/munin-node-from-hell/README.rst b/tools/munin-node-from-hell/README.rst new file mode 100644 index 00000000..5731e589 --- /dev/null +++ b/tools/munin-node-from-hell/README.rst @@ -0,0 +1,43 @@ +munin-node from hell +==================== + +This is a simple implementation of a munin node (http://munin-monitoring.org/) +that is made to give the polling server a hard time. + +In practice this is the munin-node that we use to develop and test the awesome +stuff we do at http://hostedmunin.com/ . Use it as you feel fit :) + +Current features controlled via config file: + +* Respond slowly or never to queries. +* Have plugins that always are in warning or alarm. +* Extensive number of plugins. +* Run on multiple ports at the same time, to test huge amounts of clients. + + +Usage +----- + +munin-node-from-hell takes two arguments; the mode and which config file to +use. Mode is either --run or --muninconf. + +This software is meant to run as an ordinary unix user, please don't run +it as root without some thought. + +You probably want: + + ./munin-node-from-hell --run simple.conf + +To make a config snippet to put in munin.conf: + + ./munin-node-from-hell --muninconf simple.conf > snippet.conf + +License +------- + +See the file MIT-LICENSE for details. + +Contact +------- + +Lasse Karstensen diff --git a/tools/munin-node-from-hell/example-config.conf b/tools/munin-node-from-hell/example-config.conf new file mode 100644 index 00000000..180b0530 --- /dev/null +++ b/tools/munin-node-from-hell/example-config.conf @@ -0,0 +1,68 @@ +# example config file for muninnode-from-hell. +# +# Flow: +# * basic stuff in [base]. +# * a set of plugins should be defined in a [pluginprofile:PROFILENAME] +# * an instance (ie, server running on a local port) is added with +# [instance:INSTANCENAME] +# +# Instances has: +# port = XXXX +# AND/OR +# portrange = 2000-2005 +# +# mode = sleepy|exp +# sleepyness = 10 # when mode=sleepy, this is the uniform sleep time. +# lambd = 10 # when mode=exp, this is the mean sleep time. +# exp is good to emulate load peaks, it can easily sleep 0.02 or 20 seconds. +# (but less often 30 :)) + +[base] +# when building an example config with --muninconf, what hostname to output. +hostname = localhost + +[pluginprofile:tarpit++] +plugins = tarpit, load, locks, locks, load, tarpit, load, locks, locks, load, load, load + +[pluginprofile:base] +plugins = load, locks, locks, load, load, locks, locks, load, load, load + +[pluginprofile:manyservices] +plugins = load, locks, locks, load, load, locks, locks, load, load, load, load, locks, locks, load, load, locks, locks, load, load, load, load, locks, locks, load, load, locks, locks, load, load, load, load, locks, locks, load, load, locks, locks, load, load, load, load, locks, locks, load, load, locks, locks, load, load, load, load, locks, locks, load, load, locks, locks, load, load, load, load, locks, locks, load, load, locks, locks, load, load, load, load, locks, locks, load, load, locks, locks, load, load, load + +[instance:bar] +pluginprofile = base +port = 4000 +mode = sleepy +# 40*10 should be aborted due to server time constraints. +sleepyness = 40 +#lambd = 5 + +[instance:25sec] +pluginprofile = base +port = 4001 +mode = exp +#sleepyness = 30 +# mean 2sec +lambd = 2 + + +[instance:baz] +pluginprofile = base +pluginmultiplier = 2 +port = 4940 +mode = sleepy +sleepyness = 5 +#10 +#lambd = 5 + +# bringer of chaos +[instance:qux] +pluginprofile = base +port = 4948 +mode = exp +lambd = 10 + +[instance:tarpit] +pluginprofile = tarpit++ +port = 3000 diff --git a/tools/munin-node-from-hell/muninnode-from-hell b/tools/munin-node-from-hell/muninnode-from-hell new file mode 100755 index 00000000..187c4138 --- /dev/null +++ b/tools/munin-node-from-hell/muninnode-from-hell @@ -0,0 +1,290 @@ +#!/usr/bin/python +# +# Artificial munin node that behaves in all the ways you would like +# ordinary nodes _not_ to behave. +# +# Intended use is for designing and debugging munin-server poller to handle +# such problems. +# +# See the file MIT-LICENSE for licensing information. +# +# Copyright (C) 2011 Karstensen IT +# Written by Lasse Karstensen , Dec 2011. + +import os, sys, time, random +import socket +import threading +import SocketServer +import ConfigParser + +VERSION = "muninnode-from-hell v0.1" +modules = {} + +class MuninPlugin: + def sleep_fetch(self, conf): + period = None + if conf.get("mode") == "sleepy" and conf.get("sleepyness"): + period = float(conf.get("sleepyness")) + if conf.get("mode") == "exp" and conf.get("lambd"): + period = random.expovariate(1 / float(conf.get("lambd"))) + + if period: + #print "will sleep %.3f seconds" % period + time.sleep(period) + + def sleep_config(self, conf): + return self.sleep_fetch(conf) + + +class load(MuninPlugin): + def fetch(self, conf): + self.sleep_fetch(conf) + load = open("/proc/loadavg", "r").read() + load, rest = load.split(" ", 1) + load = float(load) + return "load.value %.2f" % load + + def config(self, conf): + self.sleep_config(conf) + return """graph_title Load average +graph_args --base 1000 -l 0 +graph_vlabel load +graph_scale no +graph_category system +load.label load +graph_info The load average of the machine describes how many processes are in the run-queue (scheduled to run "immediately"). +load.info 5 minute load average """ +modules["load"] = load() + +class locks(MuninPlugin): + def fetch(self, conf): + self.sleep_fetch(conf) + fp = open("/proc/locks", "r") + fdata = fp.readlines() + return "locks.value %i" % len(fdata) + + def config(self, conf): + self.sleep_config(conf) + return """graph_title Filesystem locks +graph_vlabel number of locks +graph_scale no +graph_info This graph shows file system lock info +graph_category system +locks.label number of locks +locks.info Number of active locks""" +modules["locks"] = locks() + +class tarpit(MuninPlugin): + "Nasty plugin that never responds" + def fetch(self, conf): + time.sleep(1000) + + def config(self, conf): + time.sleep(1000) +modules["tarpit"] = tarpit() + +class always_warning(MuninPlugin): + def fetch(self, conf): + return "generic.value 10" + + def config(self, conf): + return """graph_title Always in warning +graph_vlabel Level +graph_scale no +graph_info A simple graph that is always in warning or alarm +graph_category active_notification +generic.label Level +generic.info Level usually above warning level +generic.warn 5 +generic.crit 10""" +modules["always_warning"] = always_warning() + +class always_alarm(always_warning): + def fetch(self, conf): + return "generic.value 20" +modules["always_alarm"] = always_alarm() + + +class ArgumentTCPserver(SocketServer.ThreadingTCPServer): + def __init__(self, server_address, RequestHandlerClass, args): + SocketServer.ThreadingTCPServer.__init__(self,server_address, RequestHandlerClass) + self.args = args + + +class MuninHandler(SocketServer.StreamRequestHandler): + """ + Munin server implementation. + + This is based on munin_node.py by Chris Holcombe / http://sourceforge.net/projects/pythonmuninnode/ + + Possible commands: + list, nodes, config, fetch, version or quit + """ + + def handle(self): + print "%s: Connection from %s:%s. server args is %s" \ + % (self.server.args["name"], self.client_address[0], self.client_address[1], self.server.args) + # slow path + hostname = self.server.args["name"] + full_hostname = hostname + + moduleprofile = self.server.args["pluginprofile"] + modulenames = set(moduleprofile) + + self.wfile.write("# munin node at %s\n" % hostname) + + while True: + line = self.rfile.readline().strip() + try: + cmd, args = line.split(" ", 1) + except ValueError: + cmd = line + args = "" + + if not cmd or cmd == "quit": + break + + if cmd == "list": + # List all plugins that are available + self.wfile.write(" ".join(self.server.args["plugins"].keys()) + "\n") + elif cmd == "nodes": + # We just support this host + self.wfile.write("%s\n.\n" % full_hostname) + elif cmd == "config": + # display the config information of the plugin + if not self.server.args["plugins"].has_key(args): + self.wfile.write("# Unknown service\n.\n" ) + else: + config = self.server.args["plugins"][args].config(self.server.args) + if config is None: + self.wfile.write("# Unknown service\n.\n") + else: + self.wfile.write(config + "\n.\n") + elif cmd == "fetch": + # display the data information as returned by the plugin + if not self.server.args["plugins"].has_key(args): + self.wfile.write("# Unknown service\n.\n") + else: + data = self.server.args["plugins"][args].fetch(self.server.args) + if data is None: + self.wfile.write("# Unknown service\n.\n") + else: + self.wfile.write(data + "\n.\n") + elif cmd == "version": + # display the server version + self.wfile.write("munin node on %s version: %s\n" % + (full_hostname, VERSION)) + else: + self.wfile.write("# Unknown command. Try list, nodes, " \ + "config, fetch, version or quit\n") + + +def start_servers(instances): + # TODO: Listen to IPv6 + HOST = "0.0.0.0" + servers = {} + for iconf in instances: + print "Setting up instance %s at port %s" \ + % (iconf["name"], iconf["expanded_port"]) + + server = ArgumentTCPserver((HOST, iconf["expanded_port"]), MuninHandler, iconf) + server_thread = threading.Thread(target=server.serve_forever) + server_thread.daemon = True + server_thread.start() + + servers[iconf["name"]] = server + return servers + + + +def usage(): + print "Usage: %s [--run] [--muninconf] " % sys.argv[0] + +def main(): + if len(sys.argv) <= 2: + usage() + sys.exit(1) + + config = ConfigParser.RawConfigParser() + config.read(sys.argv[2]) + + instancekeys = [ key for key in config.sections() if key.startswith("instance:") ] + servers = {} + + instances = [] + + for key in instancekeys: + instancename = key.split(":", 2)[1] + portrange = [] + if config.has_option(key, "port"): + portrange = [ config.getint(key, "port") ] + if config.has_option(key, "portrange"): + rangestr = config.get(key, "portrange") + ranges = rangestr.split("-") + range_expanded = range(int(ranges[0]), int(ranges[1])+1, 1) + portrange += range_expanded + + if len(portrange) == 0: + print "WARN: No port or portrange defined for instance %s" \ + % instancename + + pluginprofile = "pluginprofile:%s" % config.get(key, "pluginprofile") + if not config.has_section(pluginprofile): + print "WARN: Definition for pluginprofile %s not found, skipping" \ + % config.get(key, "pluginprofile") + continue + + plugins = {} + tentative_pluginlist = config.get(pluginprofile, "plugins").split(",") + assert(len(tentative_pluginlist) > 0) + for tentative_plugin in tentative_pluginlist: + tentative_plugin = tentative_plugin.strip() + if not modules.has_key(tentative_plugin): + print "WARN: Pluginprofile %s specifies unknown plugin %s" \ + % (pluginprofile, tentative_plugin) + continue + + # support more than one instanciation of the same plugin. + plugininstancename = tentative_plugin + i=2 + while (plugins.has_key(plugininstancename)): + plugininstancename = tentative_plugin + str(i) + i += 1 + + plugins[plugininstancename] = modules[tentative_plugin] + + for portinstance in portrange: + instanceconfig = dict() + + for k,v in config.items(key): + instanceconfig[k] = v + + instanceconfig["plugins"] = plugins + + instanceconfig["name"] = "%s-%s" % (instancename, portinstance) + instanceconfig["expanded_port"] = portinstance + + instances.append(instanceconfig) + # XXX: need to store what handlers we should have. + + # output sample munin config for the poller + if "--muninconf" in sys.argv: + for i in instances: + print "[%s;%s]\n\taddress %s\n\tuse_node_name yes\n\tport %s\n" \ + % ( "fromhell", i["name"], config.get("base","hostname"), i["port"]) + + + if "--run" in sys.argv: + servers = start_servers(instances) + + try: + while True: + time.sleep(0.5) + except KeyboardInterrupt: + print "Caught Ctrl-c, shutting down.." + for port, server in servers.items(): + server.shutdown() + sys.exit(0) + +if __name__ == "__main__": + main() diff --git a/tools/munin-node-from-hell/notifications.conf b/tools/munin-node-from-hell/notifications.conf new file mode 100644 index 00000000..5a7b2c91 --- /dev/null +++ b/tools/munin-node-from-hell/notifications.conf @@ -0,0 +1,19 @@ +# Example config file for muninnode-from-hell. +# +# Run an instance with plugins that always has some +# notification level. (unknown / warning / critical) + +[instance:notifications] +pluginprofile = notif +port = 3000 + +# +#[instance:baz] +#port = 4940 +#sleepyness = 30 +[pluginprofile:notif] +plugins = always_warning, always_alarm + +[base] +# when building an example config with --muninconf, what hostname to output. +hostname = localhost diff --git a/tools/munin-node-from-hell/simple.conf b/tools/munin-node-from-hell/simple.conf new file mode 100644 index 00000000..ad95c296 --- /dev/null +++ b/tools/munin-node-from-hell/simple.conf @@ -0,0 +1,16 @@ +# Example config file for muninnode-from-hell. +# +# This is the simplest possible config, just run an ordinary munin-node +# with a trivial amount of plugins on a single port. +# + +[instance:simple] +pluginprofile = base +port = 4000 + +[pluginprofile:base] +plugins = load, locks + +[base] +# when building an example config with --muninconf, what hostname to output. +hostname = localhost diff --git a/tools/munin-node-from-hell/tarpit.conf b/tools/munin-node-from-hell/tarpit.conf new file mode 100644 index 00000000..3a0995de --- /dev/null +++ b/tools/munin-node-from-hell/tarpit.conf @@ -0,0 +1,16 @@ +# Config file for muninnode-from-hell. +# + +[instance:tarpit] +pluginprofile = tarpit +port = 3000 + +[pluginprofile:tarpit] +plugins = tarpit, load, locks + +[pluginprofile:base] +plugins = load, locks, locks, load, load, locks, locks, load, load, load + +[base] +# when building an example config with --muninconf, what hostname to output. +hostname = localhost From 006ef061c2704fd0b66721fa8a35b6d7a408c86a Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Thu, 19 Jan 2012 18:44:17 +0100 Subject: [PATCH 007/123] support for 'adaX' and 'daX' devices --- plugins/other/hddtemp_smartctl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/other/hddtemp_smartctl b/plugins/other/hddtemp_smartctl index 9c73668f..f5c61f7f 100755 --- a/plugins/other/hddtemp_smartctl +++ b/plugins/other/hddtemp_smartctl @@ -139,7 +139,7 @@ if ($^O eq 'linux') { # without probing them. } elsif ($^O eq 'freebsd') { opendir(DEV, '/dev'); - @drives = grep /^ad[0-9]+$/, readdir DEV; + @drives = grep /^(a?da|ad)[0-9]+$/, readdir DEV; closedir(DEV); } elsif ($^O eq 'solaris') { @drives = map { s@.*/@@ ; $_ } glob '/dev/rdsk/c*t*d*s2'; From 9a1129c0c1c00ddcbcb71561ed209277968bc7f2 Mon Sep 17 00:00:00 2001 From: Steve Schnepp Date: Fri, 20 Jan 2012 12:05:02 +0100 Subject: [PATCH 008/123] Providing a way to change the state file --- plugins/other/dovecot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/other/dovecot b/plugins/other/dovecot index 1ce5108c..7b5b9eb7 100755 --- a/plugins/other/dovecot +++ b/plugins/other/dovecot @@ -28,7 +28,7 @@ ###################### # Configuration ###################### -STAT_FILE=/var/lib/munin/plugin-state/plugin-dovecot.state +STAT_FILE=${STAT_FILE:-/var/lib/munin/plugin-state/plugin-dovecot.state} EXPR_BIN=/usr/bin/expr LOGFILE=${logfile:-/var/log/mail.log} ###################### From d5de773e4b66f13a359e41c7e0f5b25116651af7 Mon Sep 17 00:00:00 2001 From: Steve Schnepp Date: Fri, 20 Jan 2012 12:05:31 +0100 Subject: [PATCH 009/123] explicitely use bash as it contains some bashism --- plugins/other/dovecot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/other/dovecot b/plugins/other/dovecot index 7b5b9eb7..beb62633 100755 --- a/plugins/other/dovecot +++ b/plugins/other/dovecot @@ -1,4 +1,4 @@ -#!/bin/sh +#! /bin/bash # # Munin Plugin # to count logins to your dovecot mailserver From 0b18f278abfb6fd955bff34c8582cc3c637a0706 Mon Sep 17 00:00:00 2001 From: Andrey Dudin Date: Fri, 20 Jan 2012 15:47:10 +0400 Subject: [PATCH 010/123] Update plugins/other/php-fastcgi --- plugins/other/php-fastcgi | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/plugins/other/php-fastcgi b/plugins/other/php-fastcgi index 6056da02..728417ec 100755 --- a/plugins/other/php-fastcgi +++ b/plugins/other/php-fastcgi @@ -6,9 +6,9 @@ # php_fcgi --- Munin plugin for determining the memory usage and # number of PHP FastCGI processes. -# Copyright (C) 2010 António P. P. Almeida +# Copyright (C) 2010 Antonio P. P. Almeida -# Author: António P. P. Almeida +# Author: Antonio P. P. Almeida # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), @@ -51,13 +51,21 @@ Any host running PHP FastCGI. 1.0 +=head1 CONFIGURATION + +This shows the default configuration of this plugin. You can override +the process name of php-fcgi. + + [php-fcgi] + env.pname php-cgi + =head1 BUGS None known =head1 AUTHOR -António Almeida +Antonio Almeida =head1 REPOSITORY @@ -72,6 +80,11 @@ MIT ## Support for rounding functions. use POSIX; +## Environment defined variables. +## The default process name if different set it in the environment. + +my $PROCESS_NAME = exists $ENV{'pname'} ? $ENV{'pname'} : "php-cgi"; + ## Munin config method. if (exists $ARGV[0] and $ARGV[0] eq "config") { @@ -86,7 +99,7 @@ if (exists $ARGV[0] and $ARGV[0] eq "config") { exit 0; } else { - my ($pp, $pm) = eval(`ps u -p \$(pidof php-cgi) | awk 'NR > 1 {pm += \$5} END {print "("NR-1","pm/1024")"}'`); + my ($pp, $pm) = eval(`ps u -p \$(pidof $PROCESS_NAME) | awk 'NR > 1 {pm += \$5} END {print "("NR-1","pm/1024")"}'`); printf("php_cgi_ram.value %d\n", ceil($pm)); print "php_cgi_processes.value $pp\n"; -} +} \ No newline at end of file From 6df2ce234039b907d12459d75436d31bb803d127 Mon Sep 17 00:00:00 2001 From: Lasse Karstensen Date: Sat, 21 Jan 2012 12:44:03 +0100 Subject: [PATCH 011/123] Add a simple graph testing plugin, and use critical instead of alarm --- tools/munin-node-from-hell/README.rst | 4 +-- tools/munin-node-from-hell/basic.conf | 15 +++++++++ .../munin-node-from-hell/muninnode-from-hell | 32 +++++++++++++++++-- tools/munin-node-from-hell/notifications.conf | 2 +- 4 files changed, 47 insertions(+), 6 deletions(-) create mode 100644 tools/munin-node-from-hell/basic.conf diff --git a/tools/munin-node-from-hell/README.rst b/tools/munin-node-from-hell/README.rst index 5731e589..2e7a04aa 100644 --- a/tools/munin-node-from-hell/README.rst +++ b/tools/munin-node-from-hell/README.rst @@ -10,8 +10,8 @@ stuff we do at http://hostedmunin.com/ . Use it as you feel fit :) Current features controlled via config file: * Respond slowly or never to queries. -* Have plugins that always are in warning or alarm. -* Extensive number of plugins. +* Have plugins that always are in warning or critical. +* Extensive number of plugins runnint at once. * Run on multiple ports at the same time, to test huge amounts of clients. diff --git a/tools/munin-node-from-hell/basic.conf b/tools/munin-node-from-hell/basic.conf new file mode 100644 index 00000000..64c83add --- /dev/null +++ b/tools/munin-node-from-hell/basic.conf @@ -0,0 +1,15 @@ +# +# Initialise plugins that test the basic functions of Munin. +# +# + +[instance:basic] +pluginprofile = basic +port = 4000 + +[pluginprofile:basic] +plugins = always_warning, always_critical, graph_area + +[base] +# when building an example config with --muninconf, what hostname to output. +hostname = localhost diff --git a/tools/munin-node-from-hell/muninnode-from-hell b/tools/munin-node-from-hell/muninnode-from-hell index 187c4138..5106a123 100755 --- a/tools/munin-node-from-hell/muninnode-from-hell +++ b/tools/munin-node-from-hell/muninnode-from-hell @@ -91,7 +91,7 @@ class always_warning(MuninPlugin): return """graph_title Always in warning graph_vlabel Level graph_scale no -graph_info A simple graph that is always in warning or alarm +graph_info A simple graph that is always in warning or critical graph_category active_notification generic.label Level generic.info Level usually above warning level @@ -99,10 +99,36 @@ generic.warn 5 generic.crit 10""" modules["always_warning"] = always_warning() -class always_alarm(always_warning): +class always_critical(always_warning): def fetch(self, conf): return "generic.value 20" -modules["always_alarm"] = always_alarm() +modules["always_critical"] = always_critical() + +class graph_area(MuninPlugin): + "A plugin that uses STACK and AREA. From proc_pri. Use: testing the grapher" + def fetch(self, conf): + return """high.value 3 + low.value 2 + locked.value 1""" + + def config(self, conf): + return """graph_title AREA and STACK +graph_order low high locked +graph_category graphtes t +graph_info This graph shows nuber of processes at each priority +graph_args --base 1000 -l 0 +graph_vlabel Number of processes +high.label high priority +high.draw STACK +high.info The number of high-priority processes (tasks) +low.label low priority +low.draw AREA +low.info The number of low-priority processes (tasks) +locked.label locked in memory +locked.draw STACK +locked.info The number of processes that have pages locked into memory (for real-time and custom IO) +""" +modules["graph_area"] = graph_area() class ArgumentTCPserver(SocketServer.ThreadingTCPServer): diff --git a/tools/munin-node-from-hell/notifications.conf b/tools/munin-node-from-hell/notifications.conf index 5a7b2c91..61f05118 100644 --- a/tools/munin-node-from-hell/notifications.conf +++ b/tools/munin-node-from-hell/notifications.conf @@ -12,7 +12,7 @@ port = 3000 #port = 4940 #sleepyness = 30 [pluginprofile:notif] -plugins = always_warning, always_alarm +plugins = always_warning, always_critical [base] # when building an example config with --muninconf, what hostname to output. From 071b1cace3495654a46e964784f6b3ec8ff154ab Mon Sep 17 00:00:00 2001 From: Lasse Karstensen Date: Sat, 21 Jan 2012 13:22:30 +0100 Subject: [PATCH 012/123] clean up alarm plugins, fix typo in graphtest --- .../munin-node-from-hell/muninnode-from-hell | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/tools/munin-node-from-hell/muninnode-from-hell b/tools/munin-node-from-hell/muninnode-from-hell index 5106a123..83a725e6 100755 --- a/tools/munin-node-from-hell/muninnode-from-hell +++ b/tools/munin-node-from-hell/muninnode-from-hell @@ -84,37 +84,42 @@ class tarpit(MuninPlugin): modules["tarpit"] = tarpit() class always_warning(MuninPlugin): + conftext = """graph_title Always in LEVEL +graph_vlabel Level +graph_scale no +graph_info A simple graph that is always in LEVEL +graph_category active_notification +generic.label Level +generic.info Level usually above warning level +generic.warning 5 +generic.critical 10""" + def fetch(self, conf): return "generic.value 10" def config(self, conf): - return """graph_title Always in warning -graph_vlabel Level -graph_scale no -graph_info A simple graph that is always in warning or critical -graph_category active_notification -generic.label Level -generic.info Level usually above warning level -generic.warn 5 -generic.crit 10""" + return self.conftext.replace("LEVEL","warning") modules["always_warning"] = always_warning() class always_critical(always_warning): def fetch(self, conf): return "generic.value 20" + + def config(self, conf): + return self.conftext.replace("LEVEL","critical") modules["always_critical"] = always_critical() class graph_area(MuninPlugin): "A plugin that uses STACK and AREA. From proc_pri. Use: testing the grapher" def fetch(self, conf): return """high.value 3 - low.value 2 - locked.value 1""" +low.value 2 +locked.value 1""" def config(self, conf): return """graph_title AREA and STACK graph_order low high locked -graph_category graphtes t +graph_category graphtest graph_info This graph shows nuber of processes at each priority graph_args --base 1000 -l 0 graph_vlabel Number of processes From 851979ebad828989b61a3a488a326337deda4c7b Mon Sep 17 00:00:00 2001 From: Lasse Karstensen Date: Sat, 21 Jan 2012 14:14:44 +0100 Subject: [PATCH 013/123] use a different category for always_XX --- tools/munin-node-from-hell/muninnode-from-hell | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/munin-node-from-hell/muninnode-from-hell b/tools/munin-node-from-hell/muninnode-from-hell index 83a725e6..06f0ef60 100755 --- a/tools/munin-node-from-hell/muninnode-from-hell +++ b/tools/munin-node-from-hell/muninnode-from-hell @@ -88,7 +88,7 @@ class always_warning(MuninPlugin): graph_vlabel Level graph_scale no graph_info A simple graph that is always in LEVEL -graph_category active_notification +graph_category always_LEVEL generic.label Level generic.info Level usually above warning level generic.warning 5 From db72714eb4c50835f0ffcce813d19b3c234c1ca5 Mon Sep 17 00:00:00 2001 From: Lasse Karstensen Date: Sat, 21 Jan 2012 20:42:53 +0100 Subject: [PATCH 014/123] rename dses to easier check correct graphing --- .../munin-node-from-hell/muninnode-from-hell | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/tools/munin-node-from-hell/muninnode-from-hell b/tools/munin-node-from-hell/muninnode-from-hell index 06f0ef60..b7130742 100755 --- a/tools/munin-node-from-hell/muninnode-from-hell +++ b/tools/munin-node-from-hell/muninnode-from-hell @@ -112,26 +112,26 @@ modules["always_critical"] = always_critical() class graph_area(MuninPlugin): "A plugin that uses STACK and AREA. From proc_pri. Use: testing the grapher" def fetch(self, conf): - return """high.value 3 -low.value 2 -locked.value 1""" + return """first.value 3 +second.value 2 +third.value 1""" def config(self, conf): return """graph_title AREA and STACK -graph_order low high locked +graph_order first second third graph_category graphtest graph_info This graph shows nuber of processes at each priority graph_args --base 1000 -l 0 graph_vlabel Number of processes -high.label high priority -high.draw STACK -high.info The number of high-priority processes (tasks) -low.label low priority -low.draw AREA -low.info The number of low-priority processes (tasks) -locked.label locked in memory -locked.draw STACK -locked.info The number of processes that have pages locked into memory (for real-time and custom IO) +first.label first +first.draw STACK +first.info The number of first-priority processes (tasks) +second.label second +second.draw AREA +second.info The number of second-priority processes (tasks) +third.label third +third.draw STACK +third.info The number of processes that have pages third into memory (for real-time and custom IO) """ modules["graph_area"] = graph_area() From 1c70bc928e088c7cc2cb13fe8502f20b11f32c5a Mon Sep 17 00:00:00 2001 From: Lasse Karstensen Date: Sat, 21 Jan 2012 20:51:40 +0100 Subject: [PATCH 015/123] Revert "rename dses to easier check correct graphing" Really, not a good idea. This reverts commit db72714eb4c50835f0ffcce813d19b3c234c1ca5. --- .../munin-node-from-hell/muninnode-from-hell | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/tools/munin-node-from-hell/muninnode-from-hell b/tools/munin-node-from-hell/muninnode-from-hell index b7130742..06f0ef60 100755 --- a/tools/munin-node-from-hell/muninnode-from-hell +++ b/tools/munin-node-from-hell/muninnode-from-hell @@ -112,26 +112,26 @@ modules["always_critical"] = always_critical() class graph_area(MuninPlugin): "A plugin that uses STACK and AREA. From proc_pri. Use: testing the grapher" def fetch(self, conf): - return """first.value 3 -second.value 2 -third.value 1""" + return """high.value 3 +low.value 2 +locked.value 1""" def config(self, conf): return """graph_title AREA and STACK -graph_order first second third +graph_order low high locked graph_category graphtest graph_info This graph shows nuber of processes at each priority graph_args --base 1000 -l 0 graph_vlabel Number of processes -first.label first -first.draw STACK -first.info The number of first-priority processes (tasks) -second.label second -second.draw AREA -second.info The number of second-priority processes (tasks) -third.label third -third.draw STACK -third.info The number of processes that have pages third into memory (for real-time and custom IO) +high.label high priority +high.draw STACK +high.info The number of high-priority processes (tasks) +low.label low priority +low.draw AREA +low.info The number of low-priority processes (tasks) +locked.label locked in memory +locked.draw STACK +locked.info The number of processes that have pages locked into memory (for real-time and custom IO) """ modules["graph_area"] = graph_area() From ce8b1121ec331708bc5307d44395faead0c608af Mon Sep 17 00:00:00 2001 From: Thomas Diener Date: Sun, 22 Jan 2012 21:44:37 +0100 Subject: [PATCH 016/123] OID to MIB changed --- plugins/other/snmp__fn | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/plugins/other/snmp__fn b/plugins/other/snmp__fn index 1cad6bc4..5b3b9dd3 100755 --- a/plugins/other/snmp__fn +++ b/plugins/other/snmp__fn @@ -11,6 +11,7 @@ # June, 1991. # # Version: v1.00 30.10.2011 First draft of the fortigate plugin +# v1.01 19.01.2012 OID to MIB changed, plugins gets faster # # Parameters: config (required) # autoconf (optional) @@ -27,6 +28,10 @@ # Fortigate Activate snmp on your Fortigate firewall. Fortigate documentation # at https://support.fortinet.com # +# MIB Download and copy the Fortigate MIB defintion file to +# /usr/share/snmp/mibs/FORTINET-300-MIB.txt (Filename depends on +# used Version) +# # Tested with Fortinet Fortigate-50B, Firmware 3.00(MR6) on Ubuntu 10.04 LTS # with Munin 1.4.4 installed. # @@ -38,18 +43,22 @@ ### Constants ------------------------------------------------------------------ SNMPCLIENT=`basename $0 | sed 's/^snmp_//g' | cut -d "_" -f1` -SNMPGET="/usr/bin/snmpget -c $community -v 2c $SNMPCLIENT" +MIBFILE="/usr/share/snmp/mibs/FORTINET-300-MIB.20080414.txt" +FNTYPE=`echo $MIBFILE | cut -d "." -f1 | cut -d "/" -f6` +SNMPGET="/usr/bin/snmpget -m $MIBFILE -c $community -v 2c $SNMPCLIENT" ### Variables ------------------------------------------------------------------ fnSysVersion="1.3.6.1.4.1.12356.1.3.0" -FGTcpu="1.3.6.1.4.1.12356.1.8.0" -fnSysVersion="1.3.6.1.4.1.12356.1.3.0" -fnSysMemUsage="1.3.6.1.4.1.12356.1.9.0" -fnSysSesCount="1.3.6.1.4.1.12356.1.10.0" -fnVPNSslStatsLoginUsers="1.3.6.1.4.1.12356.9.5.1.3.1" -fnVPNSslStatsActiveWebSessions="1.3.6.1.4.1.12356.9.5.1.5.1" -fnVPNSslStatsActiveTunnels="1.3.6.1.4.1.12356.9.5.1.7.1" +FGTcpu="$FNTYPE::fnSysCpuUsage.0" +fnSysVersion="$FNTYPE::fnSysVersion.0" +fnSysMemUsage="$FNTYPE::fnSysMemUsage.0" +fnSysSesCount="$FNTYPE::fnSysSesCount.0" +fnVPNSslStatsLoginUsers="$FNTYPE::fnVpnSslStatsLoginUsers.1" +fnVPNSslStatsActiveWebSessions="$FNTYPE::fnVpnSslStatsActiveWebSessions.1" +fnVPNSslStatsActiveTunnels="$FNTYPE::fnVpnSslStatsActiveTunnels.1" +mibfile="/usr/share/snmp/mibs/FORTINET-300-MIB.20080414.txt" + UNIT=`$SNMPGET $fnSysVersion | cut -d ":" -f4 | cut -d " " -f2 | cut -d "\"" -f2` SCPU=`$SNMPGET $FGTcpu | cut -d ":" -f4 | cut -d " " -f2` SMEM=`$SNMPGET $fnSysMemUsage | cut -d ":" -f4 | cut -d " " -f2` From 0db07b5c4fe65c78291ac287784431dd6ec501b4 Mon Sep 17 00:00:00 2001 From: Luka Furlan Date: Wed, 25 Jan 2012 16:15:15 +0100 Subject: [PATCH 017/123] Added tomcat_, a wildcard plugin for Apache Tomcat connectors and JBoss JVM status. --- plugins/other/tomcat_ | 132 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 plugins/other/tomcat_ diff --git a/plugins/other/tomcat_ b/plugins/other/tomcat_ new file mode 100644 index 00000000..c96a88ce --- /dev/null +++ b/plugins/other/tomcat_ @@ -0,0 +1,132 @@ +#!/usr/bin/python + +''' +Wildcard plugin to monitor Apache Tomcat connectors and/or JBoss' JVM. + +To use this plugin: +1. Set site, username and password variables before you do anything else. +2. Run plugin with suggest argument to get all the available options. +3. Copy plugin to munin plugions folder and make it executable. +3. Create symbolic links based on output from step 2. Examples: + tomcat_jvm - monitor JVM usage + tomcat_ajp-127.0.0.1-8009 - monitor ajp connector + tomcat_http-127.0.0.1-8080 - monitor http connector +4. Check links by running them. +5. Restart munin-node. +6. Enjoy graphs. +''' + +import urllib2 +import base64 +import xml.dom.minidom +import sys, os, re + +# Configure me ... +site = 'http://127.0.0.1:8080/status?XML=true' +username = 'admin' +password = 'admin' + +# Timeout for urlopen. +required_version = (2, 6) +current_version = sys.version_info[:2] + +connector_attrs = ( + 'maxThreads', + 'minSpareThreads', + 'maxSpareThreads', + 'currentThreadCount', + 'currentThreadBusy' +) + +jvm_attrs = ( + 'free', + 'total', + 'max' +) + +ctx = sys.argv[0].rstrip('.py').split('_')[1] + +def site_auth(): + # Prepare base64 encoded string + enc_string = base64.encodestring('%s:%s' % (username, password)) + + # Prepare request and add headers + request = urllib2.Request(url=site, headers={"Authorization": "Basic %s" % enc_string}) + try: + if current_version >= required_version: + return urllib2.urlopen(request, timeout=5).read() + else: + return urllib2.urlopen(request).read() + except: + print "Failed to access %s ... please check the configuration." % (site) + sys.exit(1) + +def jvm_data(data): + document = data.documentElement + for sub_document in document.childNodes: + if sub_document.nodeName == 'jvm': + node = sub_document.firstChild + for attr in jvm_attrs: + print "%s.value %s" % (attr, int(node.getAttribute(attr))) + +def connector_data(data, connector_name): + document = data.documentElement + for sub_document in document.childNodes: + if sub_document.nodeName == 'connector' and sub_document.getAttribute('name') == connector_name: + node = sub_document.firstChild + for attr in connector_attrs: + try: + print "%s.value %s" % (attr, int(node.getAttribute(attr))) + except: + pass + +def suggest(data): + document = data.documentElement + for sub_document in document.childNodes: + if sub_document.nodeName == 'jvm': + print "jvm" + elif sub_document.nodeName == 'connector': + print sub_document.getAttribute('name') + else: + pass + +def configure(): + print "graph_title Tomcat status - %s" % ctx + print "graph_category tomcat" + if ctx == 'jvm': + print "graph_args --base 1024 -l 0" + print "graph_scale yes" + print "graph_vlabel JVM in bytes" + print "graph_info This graph shows JVM usage of Tomcat." + for attr in jvm_attrs: + print "%s.label %s" % (attr, attr) + print "%s.type GAUGE" % (attr) + print "%s.min 0" % (attr) + print "%s.draw LINE1" % (attr) + print "%s.info %s %s in bytes" % (attr, ctx, attr) + else: + print "graph_args --base 1000 -l 0" + print "graph_scale no" + print "graph_vlabel Connector threads" + print "graph_info This graph shows connector threads for %s" % ctx + for attr in connector_attrs: + print "%s.label %s" % (attr, attr) + print "%s.type GAUGE" % (attr) + print "%s.min 0" % (attr) + print "%s.draw LINE1" % (attr) + print "%s.info %s %s count" % (attr, ctx, attr) + +if __name__ == "__main__": + data = xml.dom.minidom.parseString(site_auth()) + if len(sys.argv) == 2 and sys.argv[1] == 'config': + configure() + sys.exit(0) + + if len(sys.argv) == 2 and sys.argv[1] == 'suggest': + suggest(data) + sys.exit(0) + + if ctx == 'jvm': + jvm_data(data) + else: + connector_data(data, ctx) From 6db166599184da8c7b7f500118067fb1d6a34514 Mon Sep 17 00:00:00 2001 From: Thomas Diener Date: Wed, 25 Jan 2012 16:59:33 +0100 Subject: [PATCH 018/123] MIB file availability check added --- plugins/other/snmp__fn | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/plugins/other/snmp__fn b/plugins/other/snmp__fn index 5b3b9dd3..b481995a 100755 --- a/plugins/other/snmp__fn +++ b/plugins/other/snmp__fn @@ -12,6 +12,7 @@ # # Version: v1.00 30.10.2011 First draft of the fortigate plugin # v1.01 19.01.2012 OID to MIB changed, plugins gets faster +# v1.02 25.01.2012 MIB file availability check added # # Parameters: config (required) # autoconf (optional) @@ -28,7 +29,7 @@ # Fortigate Activate snmp on your Fortigate firewall. Fortigate documentation # at https://support.fortinet.com # -# MIB Download and copy the Fortigate MIB defintion file to +# MIB Download and copy the original Fortigate MIB defintion file to # /usr/share/snmp/mibs/FORTINET-300-MIB.txt (Filename depends on # used Version) # @@ -45,8 +46,12 @@ SNMPCLIENT=`basename $0 | sed 's/^snmp_//g' | cut -d "_" -f1` MIBFILE="/usr/share/snmp/mibs/FORTINET-300-MIB.20080414.txt" FNTYPE=`echo $MIBFILE | cut -d "." -f1 | cut -d "/" -f6` -SNMPGET="/usr/bin/snmpget -m $MIBFILE -c $community -v 2c $SNMPCLIENT" - +if [ -r $MIBFILE ]; then + SNMPGET="/usr/bin/snmpget -m $MIBFILE -c $community -v 2c $SNMPCLIENT" +else + echo no, MIB definition file not available or readable. + exit 1 +fi ### Variables ------------------------------------------------------------------ fnSysVersion="1.3.6.1.4.1.12356.1.3.0" @@ -57,10 +62,9 @@ fnSysSesCount="$FNTYPE::fnSysSesCount.0" fnVPNSslStatsLoginUsers="$FNTYPE::fnVpnSslStatsLoginUsers.1" fnVPNSslStatsActiveWebSessions="$FNTYPE::fnVpnSslStatsActiveWebSessions.1" fnVPNSslStatsActiveTunnels="$FNTYPE::fnVpnSslStatsActiveTunnels.1" -mibfile="/usr/share/snmp/mibs/FORTINET-300-MIB.20080414.txt" -UNIT=`$SNMPGET $fnSysVersion | cut -d ":" -f4 | cut -d " " -f2 | cut -d "\"" -f2` -SCPU=`$SNMPGET $FGTcpu | cut -d ":" -f4 | cut -d " " -f2` +UNIT=`$SNMPGET $fnSysVersion | cut -d ":" -f4 | cut -d " " -f2 | cut -d "\"" -f2` +SCPU=`$SNMPGET $FGTcpu | cut -d ":" -f4 | cut -d " " -f2` SMEM=`$SNMPGET $fnSysMemUsage | cut -d ":" -f4 | cut -d " " -f2` SCNT=`$SNMPGET $fnSysSesCount | cut -d ":" -f4 | cut -d " " -f2` USER=`$SNMPGET $fnVPNSslStatsLoginUsers | cut -d ":" -f4 | cut -d " " -f2` @@ -86,7 +90,7 @@ autoconf() exit 1 fi if [ $SCNT ]; then - echo yes, OID $fnSysSesCount can be read. + echo yes, OID $fnSysSesCount can be readed. else echo no, one or multiple OID can not be read. exit 1 @@ -107,7 +111,6 @@ config() echo 'forticpu.info CPU usage' echo 'forticpu.draw AREA' echo '' - echo "multigraph fn_memory" echo "host_name $SNMPCLIENT" echo "graph_title $UNIT - Memory usage" From e12b9690f4c66807715451bfd7690453dcc2aa09 Mon Sep 17 00:00:00 2001 From: Luka Furlan Date: Thu, 26 Jan 2012 08:41:29 +0100 Subject: [PATCH 019/123] Added autoconf, configuration via ENV variables, magic markers. --- plugins/other/tomcat_ | 60 ++++++++++++++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 15 deletions(-) diff --git a/plugins/other/tomcat_ b/plugins/other/tomcat_ index c96a88ce..b4a256a4 100644 --- a/plugins/other/tomcat_ +++ b/plugins/other/tomcat_ @@ -6,14 +6,27 @@ Wildcard plugin to monitor Apache Tomcat connectors and/or JBoss' JVM. To use this plugin: 1. Set site, username and password variables before you do anything else. 2. Run plugin with suggest argument to get all the available options. -3. Copy plugin to munin plugions folder and make it executable. -3. Create symbolic links based on output from step 2. Examples: +3. Copy plugin to munin plugins folder and make it executable. +4. Create symbolic links based on output from step 2. Examples: tomcat_jvm - monitor JVM usage tomcat_ajp-127.0.0.1-8009 - monitor ajp connector tomcat_http-127.0.0.1-8080 - monitor http connector -4. Check links by running them. -5. Restart munin-node. -6. Enjoy graphs. +5. Check links by running them. +6. Restart munin-node. +7. Enjoy graphs. + +Munin's muni-node-configure can be used to do steps 2, 3 and 4 for you. + +Example of using munin-node configuration to configure the plugin: + +[tomcat_jvm] + env.site http://127.0.0.1:8080/status?XML=true + env.username admin + env.password admin + +Magic markers +#%# capabilities=autoconf suggest +#%# family=auto ''' import urllib2 @@ -26,6 +39,16 @@ site = 'http://127.0.0.1:8080/status?XML=true' username = 'admin' password = 'admin' +# Or do it with the environment variables in munin-node configuration. +if os.environ.has_key('site'): + site = os.environ['site'] + +if os.environ.has_key('username'): + username = os.environ['username'] + +if os.environ.has_key('password'): + password = os.environ['password'] + # Timeout for urlopen. required_version = (2, 6) current_version = sys.version_info[:2] @@ -54,12 +77,11 @@ def site_auth(): request = urllib2.Request(url=site, headers={"Authorization": "Basic %s" % enc_string}) try: if current_version >= required_version: - return urllib2.urlopen(request, timeout=5).read() + return (0, urllib2.urlopen(request, timeout=5).read()) else: - return urllib2.urlopen(request).read() + return (0, urllib2.urlopen(request).read()) except: - print "Failed to access %s ... please check the configuration." % (site) - sys.exit(1) + return (-1, "Failed to access %s" % site) def jvm_data(data): document = data.documentElement @@ -117,16 +139,24 @@ def configure(): print "%s.info %s %s count" % (attr, ctx, attr) if __name__ == "__main__": - data = xml.dom.minidom.parseString(site_auth()) + status, data = site_auth() if len(sys.argv) == 2 and sys.argv[1] == 'config': configure() sys.exit(0) - if len(sys.argv) == 2 and sys.argv[1] == 'suggest': - suggest(data) + elif len(sys.argv) == 2 and sys.argv[1] == 'suggest': + suggest(xml.dom.minidom.parseString(data)) + sys.exit(0) + + elif len(sys.argv) == 2 and sys.argv[1] == 'autoconf': + if status == 0: + print "yes" + else: + print "no (%s)" % data sys.exit(0) - if ctx == 'jvm': - jvm_data(data) else: - connector_data(data, ctx) + if ctx == 'jvm': + jvm_data(xml.dom.minidom.parseString(data)) + else: + connector_data(xml.dom.minidom.parseString(data), ctx) From 8497d32ae65cb8c7b793957b2a852fbe2d2d3d17 Mon Sep 17 00:00:00 2001 From: Thomas Diener Date: Fri, 27 Jan 2012 14:17:37 +0100 Subject: [PATCH 020/123] new sample image for session graph --- images/snmp__fn-sessions.png | Bin 16407 -> 19376 bytes 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 images/snmp__fn-sessions.png diff --git a/images/snmp__fn-sessions.png b/images/snmp__fn-sessions.png old mode 100644 new mode 100755 index 24e56dd2a7ca8ad8b6d081dee9ff61540a451e0c..0f113036ab576b106de3ff042667f6616ce79aa6 GIT binary patch literal 19376 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�xl8cj=dp z{LodN?thK3q`0v8sZFWqROw$YMzv+sd~aJE(!zORKW+QQozBOoJN>87IuZYx5w0?s z5a|wz1loXV>R6c753J3#5DT-W{9US@jUFQMkZc2>OkDI{*ws+P?B-N>mNfagSYO&8 z&_+IBqc5rg4$7aNmpW4awlI@yYB+=6gkk_ZEL2MFZe{>nru@vW+al=n2NAShj#lI8 z_5;@bKP?11OMWZOVFHWY{8#?Y@?Vt4DN3>w*%Q%W!66B-!5}F~J9_LGi8B?^ z%2$~TYbv$5d7Y}Co&4}H_+5)U+eIFEumOVE1mO^5GFdrRygrdc`Q?K^CO6NXD~zoz z&ep%aq<8wHP?y2A=5gf`7?dND7x-poW@i!CN_2FzYlP+oDzC;ZcGpsQ*6rdy+vA^j zEkU!Vap&W2$irPC>}9}*Z2Eq-63jwl$db6{u5$>>!Svmz71tzst1pyuA0^0 zvBO5DJk&EMdJ7qk?XYeM{@$sVoPb%K!W1TXxg|#k)YlKrwP-Ur_uW%k+s`3;IfR}= zZ|qO(wP6LwA;;qzcn03??GUq47-B&GNbpj)lr17d2_}Un2yFY!VScnph0Bl}#}Zf7 z4BhMFw7rf7XFyG@fSl!HPq~M*zy0O_1Mc#Q3MoL#Ai3oE#7nynBee~(alT+ojh6Q* zQX)lpuZe~A_v?R0xX6naFLaO!_AmL2YbNu14_Rlbc4v2RNdLRfVWTphqHDhMd>NLV zKQz3=)G~)FX~FsdFdB4&+W^`-eJLbP8*2I8t^8JqTbCICoTCI3|DP35g%4#UaRr|R zR`S=cx$=G&1~JyhgKwj7Ax^iPMX zN$vBNl+gG&9g`DO8$))H^R`ueTY2MCG6VBdNTzS_U-GMV4TD7@Y~>4@bg zIWemah-wGl0oaTiJWpaR361Av8%}>SR^VG@QpHfywD`1A=YvV`HAY{ba1RU&OoKJ~ z$+Kt9RYEK4I}KkO2rL|xRRnnnKcI{vTJKj)0jJ zaT%vhoyavjTkWu9hu-O?~C2#Se z1_-=5rzd#{%vLWX>^MQ+Pt(m0k23K5nr8=w8RyaOfuO@r7LD`6GIWf+n0J#=n#eLI zPo__+%C4<^bYC}GR)1{95%GQY-DidftCBLo}9wsB}o0zl{+Iy#mF39wur z;McosYs(Lgk;K&*VSjByAF;y1LRW%_&co{H-#qKg12M$WDJ&!Or9E8oam%yAv%r^s z$SL%o`;?l;dxCc3!5y*}1R2vPFwcD{^fao(z$+`v> zj%g=|E{HPUe_Q=GU18`;{)kds6w_VTUYu-vsi$K|`Fush*xmp>lRen!0$IHrJ54T_8(# z;fJ7h9s|D>bbqbo#3X#Bi1+ef6f*UP4}Q-}$57W1|B@dFk%GT}M4?4`5#m$? z3D~vGi)AQ#5%UL;s%UR72*o`QKMd%67}m3`H;6Z(H;~03<@MX$$RZy_MJa+?hl-elW_~5n{!6w z6R=7FU)MaG5(HrP5ASdSl zdRkeW!oy#8?%H)!Qt|~TzF-JB4+0QK0V}eyp^ela@Uvo}E`S&Q8q7t($^>%w_5$a? zfjok3e`;pwYw-8EFc;bS9hI#G?`kx-J;9X$Z@Ld61P#){pn}3q!6a;cSNwMSp4Ubw zmISk|BA2>2DJBSfP_%4jiEtZW33vem$44+a1!inNS_y3uodnuxB%gMx$p{@%2!fEM z0PO+g&U!IL07NK|lR)MR02ZnaAiMgpB?P(=cu7Big#Zh-P-L_K1KBOpHGG-ij=2m% z$%!*(T;83jUu@xz9IhbMhp|}%NFlzGc(?AIAK^c@-W1q?;(?qz$kV?{d$Yj)2n@av z(;tj`8&evoYlW@DYQ-hf<-3n_b0fosQp%(ZVFA&@5yt||SHL}Aj*Qv}+S=G4{$$8n z26Zj1&+v{0!~zY|Ui}BhyaR%3M~hr}fV>4)865$CHQ3g(?fc5Xce!*JN$PS%wIi`9 zJ2$AwUf-)Y_Dan;C;+dI^tK*0P;0cD6C@(32nKy#a8z>2c_MjMz)^6_d1no!d5*(< zJUu^7x)409#ANi^Nsz%;=aSLfGBN~PN(&}}KV!fSLt$H6nLh>}*R{h%ZWz0(tEw3H z?JMVS>-+@f=y=P_6|*IBHJ4L=m;_7*%Kd1z!Z{uF^b!V4#R&Coaw#EUU5{;xLQhNC z8p#eB^ni67D&D6a>bl#@p)#`;A?fasoSEa3X2jY(ob>J!+0_Su)r0?J6%O=doiX7Z zqh(HI*F*=cfF|AVtkwYT+e)~MB;c{F+xKLxb(c62RAtHAfF~`uug-@dIt2rI^$`Rt zGR6RPispsj?JaNwgdd7Ga?AlNgv~#mLO+tJip3-3Hz%f@^gE>K|MgpE_N*p z40l`?T*(Lr#*L&OWE&h9sRakA&7s6HGld^A8>=?J#dU*A5ovrFD#Z|tZA)uYaA;wD zjlwGHE(rCi)4AzdUQQ(3?qVfu-TwU=gL|sYWqwe*Koi$R2B;9-h(#I%%8u~de60_O z3xPu5rGYWW^mGLS49C<^i0K%t9qu4`$6FeM$_mpC#Lo_t@QRXB>iaUVqymy?e|~QN z=RcB?94qJ~;NZ#Ust^{NkcX&k+qV5!hmRV{SXYmTtiqQDg@h!+%w!L{6S}>bABxZT>>{%4Z4j-)A8j`XX7wZ@o!rkU>dF;xhCt`h9PC_mt zagJosXvDRd_jslT~+#CL#2_UV%+ zfJSM*)Yck99RjOdi@n$2(y8jb`}bEuMY8z*$4=-4iJ)QbKXBks2D838rCf-cn;W<* zxEasFAom0xA9AQ_@ffIYh;s*8&)@vvVMI6WVNWTtUTGwqf7+SH@uGJ+|(?f!k-lqF0W+#!VvNO^?I zN=n9K&2POaqB?kvv?!f&X;7zbF z{`XH?pd!%d63UU!T_SZv zQz3Lwd9|ZZ5Hy`zs0l3>=aG9%C{GQ+?Kvd+5bz5Ylo@Eo$O>pb!q0d(W>ae%A!n+4 zy&KGrL$BE&Kfs-sS)iEPxKKmQ(@95;0ETj*6OysMF<|m-zIn%_xl}NzJO>cXM!!+f z7k}>-m4?`aAijJTi+dcJu1RPXCeOnGzFN@b`rAK_3vH#PAAo}wJTfw|$eEf?v|K3a z;w>0fBPT-uMc6hKI1ayu{G~yLt3VPv^}ru9^ftdY>fjoO<8ZOf)U5rmlg-FMAMIg0 z7Vm_YQ-paC%H4?S2Q7Yj)ai-`bc>-R(6`chqj( zC!u54ZC;#8DmWC;NO3gP_crTrphcw4z9S_L@R8Qpg*$|d&aI!2x3~TC?e8q$?S##@ zC%^r23K9$4`}uS?6c*W1RwpL9UQXchZk`4v4rVJsPdH`-z@7&BrVN;Wj=*WM8ZO5~ z$uOc&5yl_)g8jDh#uAwoNrYl_Kx%;}!?wiS8jf^s+o?6Sbcrc*4o~e42NxTs#6``f zqNRqsetP7#Rn#BfLQdpB^(VOI&?i#pGI`3Y}&KoGu-cnSv0C2_8eN&;YK2 z7sV>H=>%sYMM9%h(!X)-h+bH=W@ra3W$}SMv-Z^8>vwT3^I@&~@)y^`T4UWl(Bt?> zlEKxH8<)*k>OW-*2TcM}dJd*{F2LYY#I9QwW(+5x2$1Dz!#m!+?E$0nK6Z9%U7Qd& zY#`XL>*>KR*qk_V;_xOyBls{Pr`Ny=<8Y|T*qORrM57e$ncRF4_)_Gj@)Vy$%7tbK zc0C;J7D)r`p7z|z|E*=?S-pGf<5e1qn>rhx7S9ipIRnp8Zp0G`GzdAUQ&Pfi?_GA+ zDx>i8TV1Iw0G{6au6bU)diCp1rR3=jI)*@O)@0K;ocz|Gz)t^GO5a*o9f2J$ISW+Mryx&uud}21+Kh`&l>^A!OBh`T;@TrdmxlqeLKaA#7FDvYNwLO8=oW zOw`4|5s%ojvTj#wzpA7JC$Lj$Z6p(cV9@zh7~|t*s?pSC)%$x`iLL7$ALEr8&NrLE zXLoSDhPSZGd%uA4+i(V#<6kOL%>l=}Y6fp=FYZ24@&FEid;1~Wg+uxx^v_i5%r9|# zR4=$9@VF>}(j{bz+6wT@4RD6>t$K22x1sKd-7Mwuu=CS2ARdGBv7Om}!+`;wgO9eC zG7^2n9KZsUQMT@xk6a^U;~IePfG0Rs88N}pLB!ny-Oa$k8=iXRQoHIkise!84gyxD z(ahW;S!}qYPao)2-&O!A_5_=J#=U#5n|L8JM>yOQhW2rIUK7=oYUi)yG{ihR#Ecgs zv7B$_Kzv9*V)tkw;CZia_`goK|5UXQ%DZi%voNP5B_*Y%ek)Y99eoth?SWwDu|yM|04M!XS1zes%sBu1z5fT_2J9mM From f6330cbd5f75cf8ef96ddeb0f15eacff897ef615 Mon Sep 17 00:00:00 2001 From: Thomas Diener Date: Fri, 27 Jan 2012 14:25:57 +0100 Subject: [PATCH 021/123] new sample image --- images/snmp__fn-cpu.png | Bin 16039 -> 16093 bytes images/snmp__fn-memory.png | Bin 16135 -> 15698 bytes images/snmp__fn-sessions.png | Bin 19376 -> 18473 bytes images/snmp__fn-vpnsessions.png | Bin 18150 -> 18958 bytes 4 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 images/snmp__fn-cpu.png mode change 100644 => 100755 images/snmp__fn-memory.png mode change 100644 => 100755 images/snmp__fn-vpnsessions.png diff --git a/images/snmp__fn-cpu.png b/images/snmp__fn-cpu.png old mode 100644 new mode 100755 index 91cdc83516afa1632a11d10e018cf91c1b6e9187..a2f50b0d4065675105af49a4b378c852e30d72f0 GIT binary patch literal 16093 zcmb7r1zc6>*7rdaTR3MdrL0(=o2!-JpD zmk2MzH#{9FNiozO@=sK8!fO=j0_wKd&HMJ@GZ9Y8_uF^(260uD11?sI6P919mr^yn zE3cV&H?Sk=UN^7Cxy;vb%AxHGUe&BoiXH64sb<#=I~?xJD33WcEIcDi`Ym>REg|HJ zkH@JeWhhZ~EOriG`wU%{cWANRs=9OOdvE>l;x#9ij-oZ&j@^`OeU=BdwyVDph#aHE z#3X+F_#`ly>EAQm;0dngaQ0-KaUc-fOc zMBp437QXp-x5O=pB5#d8%x^oLk>`TN_>$yu5MB0E&rWPN3S^D6ry;s5i^2F3v1?Zx zZgnz-_nYFBP9LKkxmI6a&to%r?mi>&$K>RpsVOE0W(tcI2BYkP%18qTB^^%kr!~_l z&iSLtTYbT-8Wpd8lOiA?@p9eS)ShUG%d?(%6)VKdd`eDW;c!C)|7G>uzCLwQRbr)uG2UVZ<=bAl~4&yrg`0-=CtyC9N5g+3#%* z$96VXUJJbU=gB-C(`>M6m#sBp<+j?b&tv`__36{67onlPG_LD7k#4*B;x0Be*FR-t zvD?pU3G6JlirCcD)>g*eeoa6@;TIzrD3abeT)Z|=*^#Qo@LG+}f}3ANRax|;?DS42cb7b;F(jVdV_ z`1~+cQFo>%n@qr-&2f2LyJj)1&ZKv*FfHv&qu?5)h=>Tm`SZRHk`+T|u3ouPHXJA% zFi`A7$HqnwD6n{pRkO%IvEl6~H#fH~vT8$K6aOd1^%gSkYd%zQgp%L(lw=@v72NI1@Ng5`x<=u>=Q~?# zF$D!ZZ|YTR$6vpGE&t%bQ`n2&n?8v0*w06G?=swrzrfB;sNeWr95y}P8A_k0CrRJc zWZ&PddCO$6JT5;ySW2_8u`$22R0(^JZ^wCPXz19nV`b>vey_CwCw$nz@h+F;-ebPL zq?4I5E1cmU!6CDi62Tp{w8XkbY5Qe&XUk%|iI!5>m6G(TCZ|Sedhsf`Vv6$bK|v&~nM{f) zWz*eREUw>5Ryx$KFfmmw3|B9}^Tuw?m2&r*%`Ej&_}O1Y3UU4N(~oGJbFzhZVl%kB z1Pz)lm<*Osy?pr+<<6nsptU&i4Jz-=?Xa*gKX~XNZ|xrA4!HKpaQAyg@Y_YZ?d_u6 zx3``9ENfX_`MaXgEMBK6yt4Eg?*s{(bg?;a&E%5tm|s9eeg1sTuHS}qadDAvXB#PJ zhs8@j8Y2UTD!p~9o)M$m-wQfbXt9-5k1ZCPOG%wPbm-7s6%~ewu+mat+nJuHw4z?t z_V=UI zH(*&)T?3dJ)%3?SMkw!mP)xR%?qWYh#)c{z{qXGD>&wHiRdG695Z%Dy}=YZ;pqCV1^yOA1qGkEl~gNlD(ukFXEzM)d_L>7~1l+nZDA@oU$w zUmqVRE)2~n)_?l+=`Cq#4Ad9BikuoX^{HdW*qm;qW_^-)PWJn|cjPIW#XX-JtE;P1 zls@?X_@UP-<4akxjy)M07e`A^k5}o9U%fihE3?JNZx#`;*3dBYwrlf(o|u?tcd68! zJJNH#*GAx7v9YmQvwium{Ecqk_Ek}t*?ZWvxH01^UPK`jA$yLFho@`WaO%mELx+zX zIUcN{tZd+z-a1jYDhGWO={mBJLjIGJM&m1vSqT*^&4qSm;e0kVDe`FH4ue`zCm|sr zz5WPMuT$kO=_IA+va+&L^}2RB9?f z+0R#@r}OgiQgK!X(nu;Rhdc_^D6qbXMyF1=h>Jh%-j$D$&|dmce+7CS)G5*>Lso5U z2l)8sA|gbS8V9>QLT1t*M#$>_XoaS1UVpt4X=*&y;{k3T z1`oY>e!@EXN}$l@mEFbQ((kYj^XwNc{`~*UYU-?y6Wv*t;nT9@@4+7{p?k|~Y6dJW zS~4qVl$p3`Xi#l$Z=26#cVheUt@KVmy}!{Yuyjh(c|kl}(1}DwMux`%dpKS$(lq=1 z@#Dw8e*1Rw-aVp*_kw;ZS$ac5-ZWe@Zj0Y8FZ>8Ii_XhK`hxvZJ$I^*gQkXte|xf0 zSb@jrCxhmTMSHtj>(g0{T5M#{FWuoG_pY>UaWo2eU@&5j9zXu@;RDX;)2F}I)rslo zT-0j_XGo$Da^_J?*Yxk{QH94;wA>=4w?%aJ?0oIj(x}W#LR3>*+oPV&ZilJqqFP?> zoD8k)hfTMB|e6&@iqn_y;*woT7-e)S?9-2%K&mE|%6!|`Ir zl?oV37S&XCTCh-hTl`zwtC))ee#LSLOXXbHRj63(OG)Z-X!sf$#L*XKEeH12tA(+! zB{^!FifKtKt|wb2eIgcT>tA)cL5LrKS#0Gb<)uSy>4YaIo}dmBmKnwK~kf-mj>vT-X?k zJSmttXPW8bG(0us_vMR3>s8WocfmMXteqKQ^2MR zcSC%M;H{=lva4j7%O#55W}QEea;Nz6+`>WzE~%cXJjs>Yksw5hGhT`;v4pMHC8 zFS<6nvmj3b!0D|qb7{&O9SM=q1&PPn#l^pFF5gp9@`hi{u8pn;P8K3cXJr)8E8K|S z5Z*XkN$q${kWb}gDEz$zr;Uvblqx&Rz2s&CHtX@b)>9qz*ahmhzEY|9DV3Gy!T~GL4#sqi;f2u>}`9pPe z=HvlOYfDc51ljJ?mlRSA{;x8}BSXA)eGTE;m;Rlsw;w+;0;-LMk{a*HW-={WkhuTg z!ENd}dH=j~6B83KRXigjY0eT6$#C@FLI+Dh=hxxmeYY9u`bmwBJ4k{2H{E7o_R~8= ztxdx0{)JomOoC3UVw#%NGi4(qAR{~WeR2KfadGi(sGYMyaygoiYWzf zUHFPq3D**5msjQDbNB2lEw8Q44F)zf-KFF)M=_+z1xvv~YHGem7V=$(%Gev9xHl5t zl$YhCMfWhA@NIZ@xBD5?vC|{^*;7Ba^h>{(YYTw0E&}!!5(>7O26Q()P)tEYFU`i^ zK*wRBRLL(tdi~CqXt?QL_ine=s5tGy7ZGaDREIfE>-n`xKGYt7ne2Oki3nY6nwMR4 zjq#i3;``S`Ei|u?n>Tn)%00 za4aaoQ~uC8aaU0h5B0UNG1$4jwl=F^@MQt;lOC!Bdb-NgDapNYxKQuLEwdRYc zjMt6evC#1rW(e`kt&k1}N~lg5>Z@k_xmSyx6*u5GL%RGmlKl;Z?5n&&w}vNweuf9F zY1fEyaR+=E|Le;LL)X^^-2$QAAI8HIiD@qD>{MD*POrh(&Gz}KndToM_s4sESq|A8 zs2k~>bg-`Q6AKHArl}PX3;W9-i~jaRDgd%Kr>b$EJ9kcNZlDWV--plMIB^Ro9H{#MFB zcMAXvk%@YrnQ3y*t!5%$05C8-8vIZ~S($8gw%@O&<_^leT{!`s;BVUrhAW~UZvAMeX6*|uRw|v2Ic30x;*2Z@kr&rd|B_jtEts)Gg?rSJ@e)Z zQ$cHQmerVST^Kk1W#~a-VuykGB4n|pMFAPrQBh^l4MDTZMMVP8W_mO(^CElx`6+7} zjxVhKead$X|7`=Ntu{UoW?y6IOA9?ZQ$rH&baEo480!sEQohR_|2LffJFp-LEzAT{()q|V!6w<8$-QXM_Qk~Rr@%dK)v2Al}hKz#?k z2fL~)!fwFOHoS93R{dN6G8LhPkpHEH@Yf*Y-D%BKI)tr!nkT@w)?HWRfPSz%-A&}+ z;9y{4BBrZL*AUL@0R-(1b)$3h-wFslNvy}W|LlebJtI;+#&DEBHq zQaYeFFid=WeQzo#yt+$yP~TW>>d@-FrmIGp&>=<(*ac^ME!PRQ8p<6;%OgF#ipEBK zUteFJfB=$bbYo-0mNu50madw#2aZ3WF^Ne@zu$>_(rj=TWj%4R|GtwreF=cTOP4O) zh-udP^8EbzcMp7!_dIZZ|NVCqt1u-H-0qvb>hd@-ihd*>A6l!vFL9gYAbk|-rf~R; z%lZek&cI#Y_CLRFccv#5QA(tel#Y(vcmpdnR88ZCK>#68*6sD-Kw#TZBMX}B^sb-{+L5o=Y_No+_E#m6b!C2Q z1kRk;+s-TJ{q}JY0+0Ioh1GZuhGGv_{OzFgN_yo6W+TF!>_6H-p2%PQqr!Vs(5JKu-Gl^_KNa4>8nrl|T1@58$tF!`%@B&sh2= zpv*qS_r8vK9h3C!>x=6vQ3@+DZhtAsVWU0D_K4k9IbmQ0Q9l(IP%1#&I%GHCkkD;` z?w>Hhs11c6s%yTvpauTH6|Meu%9sDpe(_eUU<6SI)`V80BUbaJZ*sd))*22D4iEI) zWk*4HcE|>6?VAAh_{XygsKY?_H*iFkrV47l^`-EaXnhFJ?7hQ}^Qf|a!>qt%ZeKZb z&~ob!5r^74Q9Dhur)U(C0OXh0+ueB?8v1Po!7e<+`VA4YE1~uj!aK`Epb0gBeDLQm z3!G3=RJ3j4RJvc5*dAS_b)&w-1$ANLU8zGBf+j&&>847UuEg&za9x&5`O8;*vp#YW&>9G&@TY zdzMyK2+~tmKP68u8}0+-nbato9FeAWR(nHzdk^i`p2dCo-CncO&>!5#Pn@U#QIC+A z_$jrZV+d8Xv^w5FbJyfI4h|w(T3SRN+}_z?2ept=$XR*OUJsl4C*%axZK8=f5&$Xgn z=riE-@$>W0OecMIAdXhC7pLEyEu6z9rEjc+!&{{zLc@Yxg1DpIr)lfv@0c{lRZ?h%Q z+cSqLjwZay$SC6I$OoMoS)1*x!96?_*(p50wLe)gXrBBAVN8r#ZAQ&HY}N&2&bPEb z{NzvoxTub@upm$Ivf^L9G)m(aEy=P~w}W2e%BMLerq0NnNFSG-1N0!YIZlTtpj)dm zl^~=CLVvk<@nXet%*LO9PUn1%b;8)_C@lz-vzy3gTGa20*z-x+Y4JA%5W!IHQC~6UYc}xIOb1J) z8u9i3;-OHd@Nf^|;^LryPriNomg;joF)>US19q~zS!Oh-TP0rSNL2ghZfO7;=(lKU z!FIru*ZIp=%Zf4MRd4y^Dl=Z*yX4=<{=P_K1|=jTadaPj=Gq*xc0V`E2;Lp~2R4W;%8z zX_>;Th%huXbRAd=4(i*tZ(XHs!hq*3zL(*E$8hXGXgS7wiDN+~j7z&YS{%h{mr&Tt zlNa*~%$WxJ%0=)IL;E$|xDb~7{eYG_#>`{q_%9t~=9)zd;X$aU0sGMmL-B{EQMRcw z576!tNknjru~T9bwZcx!J)S%Og`)OCvH(}PQ(4jumV%Z%9fkgvxYsnHGrtkWt%mG*P`&ysYJ|3%5M2l}`R7;cROfxoGr)fa)icC$ zfj$U2E|tfo*rtkRD?$VkHWO^+hCz|AlhY{R7ngk4?8P zr1?L8{uu}$-`hKn?ZRhuzvS8M{FKp_8!2{b>+7G&CWu;Cu)pyFNuaAG;(c(iHxP@$ z+Ms>%aAS>0FV#x%;2P(`rkela&43imrEmRBOZqEO`p3JjV7j(oRpq9nww~GFSrib{R%mA?n@q(`~GFAYVdIp&STolPw&F3K@Wao&9 z5i=6}o`VIrpB%5(04)9hD*g1lg+oWo0+&LOG5mE2aMp zDUHAT7gCDS8x%slaIPOL>Yj9!oCHI9I^f>wkoSLuOK}UJ&q0c!H`lBhy4dgF_|onx z%|Evg4`?EXkeSGd{ume-_^s~mEP?VMme7DKCN#I9)Z76PP*F@vHuMnq0>;jNWy~Rb zHjDtw5qoj2bdMU6BzP##r%!2FSWXiY6L)3l2R_0zWjc7;LyZOeK?M$0u@1y14a?Sr z*`QD?ENM{+ByBWYU{+ zT3%irf)zKUrBAiAw*Fs$yb!vF6A?jh@lKU>K6LYkE)yLUg#xP;Qb5(eq!(&x<+gbT35M83D|0NK0722gAtT4gzC12pWbLY-HjJ#n$ zI!FG7;Q!c{|5pSbxA1J%8inYEKuo-TlM4DS=e9|Nx^RF2q9t!8W#_bo}34F9kImi z*N40zWC8y!0A9KHKdZ3+=Y2_yI&g;liNOICi*s^uA#fR-YSfF!$g3Ogf`j#KO6hEl z;!}U-mz9z5LIk#-noUu2DTKlRgq|ZM6*c^K+a{A338<&w8WRc>)Y8lfwTII|B*;Nd|^r>z1eIVNY-GFhzDIW@b;t@P|4&OSZMiYXbP;G!pZ&-e*+46a z9h2WX_YF+IR>=je#RY_sLN0v3QKfaUV+|3N;1M$$@zTqx`=;vl!=}Ut4u#~QW+J7{ z_U1s>LE>4UTpoPhhTlrK{zH&O1WvWSo6KXxy_ z{?>BHE7#=%^C3b6Tw)6~4(_Hh@5*^=8{cgd-&FW_oMUT!$;~K%knsf8)_j=i3Hn1ZLha? z4r>`P%5E=3=GhI2oFapVo_&3&9Y1FUj2f^Mz<)3>H3gOkmHeA~t#HF02u1-?r%xL& zUMbs4PQ39zoA!k34)IQU=>T>Lt*!l4$W7A{I-&pW?zpOop;CBrdHMQ>&u!>Go!dyc zbWNY*_}*sbY>uUS0g>_O+u7t%Ft8E%&bCuDKsF$L_xsKDDoj`0--8Jn+fHU(Y}N})qHWL5 z`?&r%5C5B0D7*T-T^E~~nR!b_MjY4)2uJL)^QMV%kwVWVMhwX1B6ALkB&>65O)E?9 zt*T)z(eoAK94!(cl9QE{aCQ~|%E@jya?7MIPd>58I4xg-J%X~{=Jn;?1F{kt+gp_d zHlHSGNz+X+l@%2)f`ar~Ga&+&_}X~b*%=pCL*qywY}%W9m?$?L_0(Ml8@mEQaKhT! zAGoWg+S(8w%RH7YgS}(FbGuTa??__%{wo2KBJm++k#S1 z}cd(l9Xm=ld5X2Yn0qVdhL&_j7&^O0P zks=Bv`@7U@PE>AJ}P zE)6ra?wn1fae%VO?25k3pcl8>m<7%`?yA8-8YDS+InwzF*mC?SeXm}fMWWF5YyEaO zA$D@-LD9?+I@jVVLYXv!*~)0U?$C?QMKYeR$eq$DG_Ax=@OD1bS7#Y?Fv0Xc*N=1| z0gq7>^d_)j_@>Pa4KoYtAmcU+;$jt8pzMY%v1hh=>8o(GxEKQ3Y_TrNlMT+>3)M(! zJc%&h)V}`Z>eu|;6pQOFW0{0@9L~;s@6SR)r@zoH1`s}=olK2UbXNw>y?E(zSfYgl z;hk?-c7@ki9>-6VSQYCDogLD|{YP^?Lc>i#9X;qkNn}SG;ct^$yti0;H4{R3oY${w zLuL|5=&=OQYWOxAe9qeej5*$4n49z*Qgd`dLeyZ##ox978HXt!mQ+FaRfM3o_MA;? z$tg{%*wgu)gYCN!om}({5)hU+w!pToMS5W={h*YnHXV|- zvcF=2IliEk*KmziiQ5E|e$jPnhMQSEkAS<+92W_4m+s7%rKO#nI1G>Tx^V{z1v$X} z56!liYdM(Ak&zK3qk8#rb^8O_h^Fqnt8(uC>kq^r+5#feJd!5{<&aR7*5^t!hz1rD zEx$okHN%U!5Q}+zeO(KJb7^TOGI8Yg_NpYd8L@x~zksZS_&A8gTpPrK+mtDlv*QHU zFNLTblJo|kj9wtaszY&PSdQLlJ?o2B&$rg?&4Hi=WN0ZNfX}g|gXO*F1)-zwh!6pM zUAh+u@j+?=7lni#oM!WnqRhdw>Vr>w3_JQXOa7&kqTyxQdF3>5d~ujts%Zf~4Rzv@Um=rNGv776@`*vKobq zb2KI?ttVQJB*;cQQB1Bvt|9oICjfGkU?6Ej!tZNMjR<6KP+F61NnM`}iBMXt@iGW~ zLm_!`#Jq-_GMiQzD(jPd$(wTI-X6=>*3``JZq4b!4WZl-A%>Jyy$qqvP&-9nyXcWC zl^wa8cG0w8f_}Ou`{Yk+q&J z-HhE8c7a+#H1~!`;Xp7}zz(^uq=cK-=XA|>TG@GTXAOxlCx2{!8nKvt6^DmL~o z2#%VzCE-Vbq z=r?SW7Pn)c7nfp}B)q&hjd1z2Gc^sD$lbEPbFoZI}+Xo;%+?I52 zeqm!y=~`p(6taJy6}!VyVqtd2&maL7MD(_qQapA3+F|%3$Bb3`hK?N{Bq2j!{UG+R zR}TBeVtrm4B8T=UaC4{a=sXEXqk%*zBsG<~xq;zWP{4<&)Iv&{)f4{{At4W98tzv& z^aOlyMY@4i&Xl&ua|%{=xf6(=X4G~CH0;*TJ2iQj1)#OqlC4<}5RqNh`n{q5)bb4s zSo%2IH>lStJ#=}BM5Dm&%^Q2~^y1}99Mn48mM^3O3^>W3pXa~>0~CegLnc4rnD6_`=)nGrhTRW}~3hVm{}5Wt!MW=Zp9Zk#RY8iUG|A z7|?;(A>xaw3BRf$%ziJqz2)@ka0|YXHJ7e3xcE6{g8`%uoe80~BN}AIDMb%TFwG9n zuy}v<9;*9L542*q<(&%k1dVMQ7ea!P}U+p1yvt zRsqPt3ZvjiP{O0^G)J)c2+7ENkVqbwbD*%ku_-DZCD@n`&p!(Ow*6#M!upc0T<#{7X))7B z8o5Ix5R`%w%=a)rr1@wsoE5rM-S=FA4sQ2FM1((ttu@lZ107bJU0fnGtCI-x6E`Q^o6cTqwvUqUaPXW2Plox5i8_~H0MRlt*O|zlgAE<+pV!vc zuYkl8{7A1#K#C3#F0+k08Sl3y`#r7=(uRchcxwWF$=0ZdWjr&1yOTS&` zF1f!UMy(*ErWST%(su=}dXb&I*(B`jnAENZTJd|OK618hiNG<#-=7mf2aY_tKT*Pm zzoLZn@_Pw-t8I=%os|FAPznfTfl7h;UWqS^fu#BO zxw*k1?wlE$oHu3@#j2?i5FdK-2$XpCRjmah}k738+0o~+5uF6O(F?KV}`}BMDnOf&*$SXcswB7 zED=bJoJ#?P=-sN)g9pe#kq}<1_!LJjPR{S$`jJ&&BeJ;SIUz};Tepr;yDZCtI+Q&6 z=+Pr&cK-@ymbh&3u~eT`*i~j4d~Z2Y2J5TCfesZ1Ry5$Y=h7Oyfw*?(IrNajWJskv zrkQyto`tB$i0NxJ)^J_sBeElrQx_1*fZD&2u=3_(W8~hpeqA^(5uEc-&bN%~j-^`d z=v5^}-@QwKoC$$55#6I|B9lvpZa@Ome6o!J{846&g0}cY=KACK%I_)qs0W;vWKK~F zkhyIS)4(TDV<-1VWM$cKp5Ng()dgDGawFcE&SNkNoC@RfRMV~{1P zcGp_5ByLk`!-NU_x)^boyXs(0Wf-+Hs^^*GsO6egfmVz{m2UOVWdLNNT%sUUDe*?M!~RREf_;jl`4tZq}(&8`9)CRm0Hhb6a8$)Jx3 z_gzf$M@dY&KOKR?d!Ee(Z`$2^pY)j|`^Uy!0ysqnLOud=(YdP*z2xRk0^E7U z5Yi7_dE&Dw0#Hw@{@ry5E_k*N^QqC5by&u3XWE@UWGvL{7cKSjtcZX&Q{fYT2|7)O zQQ~{S!vG(9hLmY&XqKls@d0&n%!Kh;(L(_9>E`C9Z0TqCKsI3CRKj7gH{Wt!hJ_iE z_wLMB;^Ux8RMS^ z1IJ=@TE%*9KqF|NLt-#!7!GiuJE)B3TtV1_9*Q{)8wEM(4BmcyPVVD|nZ07TATT2> z7=2&s>u0PZ1>tE#6&0zky6TrRhZEH9zwOj1F)1l2Zwd8hupE6>je=C~GiFR4>W2%t zkOTYjnBMUucnmDfhXNX4plN>rz}!KT!oWa$s10p4GCoSTew$94^?X_VEk2~qfMguf z8aSyn1-JI;=QZvo-6!L>Wxmh&wtE#0gd(vQ7-zP(!dLrel}vG214DED!*Yvlb0P__ zeCyVLISH`ZaEJ|fBBt^$1QCIdPyK(M+Wup1AP2wWAf^b%fC>v(VagoK#HHc-j*J`q c#XX$SRpDEQYEHuGbQJ2gxU5*3h}M(;2m4VCng9R* literal 16039 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 diff --git a/images/snmp__fn-memory.png b/images/snmp__fn-memory.png old mode 100644 new mode 100755 index ab57b1aaf687f421d9f2b43dac66354f27a236c2..198465835c302a3c9958895168ed9c8370c5cbc1 GIT binary patch literal 15698 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 JDbl)+{vQDb81VoA literal 16135 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 Tv;sHVsjkSPWD+mlee!<*^Gp1s diff --git a/images/snmp__fn-sessions.png b/images/snmp__fn-sessions.png index 0f113036ab576b106de3ff042667f6616ce79aa6..e6a6153e4716171440147356ed01f24089e1b3f6 100755 GIT binary patch literal 18473 zcma*P2RzmN`#*lPh!P29CPlK6k*yROq9|mKGPCzQDza(G$S5kCWbe`8BrDmQgO1I~ zIQIEp@4CPD_x^nDzK`GkQ9V35&Un9Huh;dwuIu@HUMJ|Liqb(!CQ1|vb@1wy%W5bT z83F#$QS5_9xGLqA;m1DX>q?hVB;?PVij)^9)N$0+%NK9E#m&XLdmWkB#>!on7T5j4 zO2Kilmi5BHCzdp1*N+ExrN7zYW0I%ZMW(>F>u~VIPZVvaG&4lNaTGZtiv|>uqXkDagxfz;Am@MBX!%UGgI4mP|oAyel+rd=gyotV>jKwGcd08E!RZJp#Bo_oS00#G6qz15S=^) z#}1{GJ^a9^8gZ+@@78$EyAQ!DDL52ATC{W3Of)n!JZg-Ar!t9XWOU{bRhby!p(`pX z^-no2#gVSpsKiAxB^=-0U8(9fi7l||wwvt{(&A&#Eq9|OBO^=d|3t@~o~xa;;*&q$ z%33ydwU8>O8GLExwFO?iU-4$dhI?FZ}6L!ubBLp?ak}SH6a%;s0`Jw@Gxpk zkTLJhj-Om*q2rD<&*ZjE>)uY0+ujeam3MIwLyc%wZP07v=!d<0`O;&fuTNXI*p93* zN)Y?y87E<`XgEEb=hUgaMZ-Q)m0LfrUsh0fo0)k)%4N2i*|z*4+3tO*LxLO_aY`zx zwlY`S{0F^wL?x%Mx8|CHF((B_d%m+(F^_0T#p3XbWZU7&wN~^FX9jBlT*%**Ai^#l8w53>+LBH6~?h1CFgS$ab#I4~BgGdacGM z&#%Ih_*EMp97o*gx2>vs#jo=;F)>1k&3#X+9H|`V^75l2UIo^MJb;?Dc6|vSPf(7R zq|d3^k+ih5jO(x5*_eqwYxHO&$Z39{q}XnB7d5M--$Lak^|05{jrbKc?3?d}SH|wO zf4K2T&RC1((1p?bjLSS~COz>|uJ!d{%(TqRR48%xpC_MCvj&@B!zBu^5$rjR8DA_5 zx@M~NqMWC`?|psVR&jH6jtrHe5?{;eI>6C4Wl-sL41()Rb2I%QT344v>_LyOO1vZ) zDmWy>exZVRf}0yVHdEy?#}v*c6Kv7`;jLL~g4^~=HyJ8RyFmW#-D9yLB|c%g>gtb2 zMhss@MTKlI96cJCQ@KGoSn9mglYNw)o?ffmO$ZKKAnXww$kAs?Y&Eb)G)Iq~o>_63 z={op=Tebf4A-T{(>;A6Zst!p_+lg|bSxel-n>T5EXBO1^3$1TyX#B`!71hepV3BsC zfzyXVjgOBTPJVm4HX6W=LhasnpxSRg-QxHQRmtwT;VSm#*XMJJas>2B4ae%kTD;?> z-4DACI!Dc#+`Ovu&R$(dCj_3sDIsy}wU~9f=KB^4%^(5sb2`KWMa9KEy}i3Gz(U>9 z)zwv+vB<>6CnY6G?qzFhYjf^efIWo6stBh)Q-k-!i4!-~)J~MBzHWZ9w&sSnUWHi3 z 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 ALjV8( literal 19376 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 From 8051623fe4a7c8566fe94aca87302f32024dcb67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Gross?= Date: Fri, 27 Jan 2012 17:07:41 +0100 Subject: [PATCH 022/123] Fix lvdisplay path on some hosts. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sébastien Gross --- plugins/other/lvm_snap_used | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/plugins/other/lvm_snap_used b/plugins/other/lvm_snap_used index cfcdc241..87574a24 100755 --- a/plugins/other/lvm_snap_used +++ b/plugins/other/lvm_snap_used @@ -14,17 +14,19 @@ #%# capabilities=autoconf # # 2011/05/20 - pmoranga - initial version +# +# 2012/01/27 - Sébastien Gross +# - Fix lvdisplay path + +lvdisplay=$(which lvdisplay) if [ "$1" = "autoconf" ]; then - /usr/sbin/lvdisplay 2>/dev/null >/dev/null - if [ $? -eq 0 ] - then - echo yes - exit 0 - else - echo "no lvdisplay found" - fi - exit 1 + if test -n "${lvdisplay}"; then + echo yes + exit 0 + fi + echo "no lvdisplay found" + exit 1 fi @@ -34,9 +36,9 @@ if [ "$1" = "config" ]; then echo 'graph_vlabel %' echo 'graph_category disk' echo 'graph_args --base 100' - /usr/sbin/lvdisplay -C | awk '$3 ~ /^s/{print $1".label "$1" snapshot of "$5} ' + ${lvdisplay} -C | awk '$3 ~ /^s/{print $1".label "$1" snapshot of "$5} ' exit 0 fi -/usr/sbin/lvdisplay -C | awk '$3 ~ /^s/{print $1".value",int($6)} ' +${lvdisplay} -C | awk '$3 ~ /^s/{print $1".value",int($6)} ' From 8ea0ef824838ed8b6c0eb37c028dd5888f7d507d Mon Sep 17 00:00:00 2001 From: Kenyon Ralph Date: Thu, 2 Feb 2012 04:18:02 -0800 Subject: [PATCH 023/123] make the graphs more like the if_ plugin Show traffic in bits instead of bytes, use negative values for incoming traffic, improve wording, simplify authentication, add documentation. --- plugins/other/tor-bandwidth-usage | 181 +++++++++++++++--------------- 1 file changed, 90 insertions(+), 91 deletions(-) diff --git a/plugins/other/tor-bandwidth-usage b/plugins/other/tor-bandwidth-usage index 26a5f070..316d57ca 100755 --- a/plugins/other/tor-bandwidth-usage +++ b/plugins/other/tor-bandwidth-usage @@ -1,6 +1,6 @@ #!/usr/bin/perl -w # -# tor_bandwidth_acct - munin plugin to monitor Tor routers traffic +# tor-bandwidth-usage - munin plugin to monitor Tor traffic # # To use this plugin you need the following: # o Enable accounting on torrc configuration file (even if you dont want to limit bandwidth usage, @@ -9,145 +9,144 @@ # AccountingStart day 12:00 # AccountingMax 100 GB # o Enable CookieAuthentication (CookieAuthentication 1 in torrc) or define a HashedControlPassword +# o Add something like the following to /etc/munin/plugin-conf.d/munin-node: +# [tor-bandwidth-usage] +# user debian-tor +# env.cookiefile /var/run/tor/control.authcookie # -# tested with Tor releases: 0.2.1.28, 0.2.1.29 # -# Author: tazoi , based on a plugin by Ævar Arnfjörð Bjarmason , based on a plugin by Ævar Arnfjörð Bjarmason # # Parameters understood (defined in file /etc/munin/plugin-conf.d/munin-node or in environment) -# host - Change which host to graph (default localhost) -# port - Change which port to connect to (default 9051) +# host - Change which host to graph (default localhost) +# port - Change which port to connect to (default 9051) # password - Plain-text control channel password (see torrc -# HashedControlPassword parameter) +# HashedControlPassword parameter) # cookiefile - Name of the file containing the control channel cookie -# (see torrc CookieAuthentication parameter) +# (see torrc CookieAuthentication parameter) # -# Using HashedControlPassword authentication has the problem that you must -# include the plain-text password in the munin config file. To have any -# effect, that file shouldn't be world-readable. -# If you're using CookieAuthentication, you should run this plugin as a user -# which has read access to the tor datafiles. Also note that bugs in versions -# upto and including 0.1.1.20 prevent CookieAuthentication from working. +# Using HashedControlPassword authentication has the problem that you +# must include the plain-text password in the munin config file. To +# have any effect, that file shouldn't be world-readable. # -# Usage: place in /etc/munin/node.d/ or in /etc/munin/plugins (or link it there using ln -s) +# If you're using CookieAuthentication, you should run this plugin as +# a user which has read access to the tor datafiles. Also note that +# bugs in versions upto and including 0.1.1.20 prevent +# CookieAuthentication from working. # -# Parameters understood: -# config (required) -# autoconf (optional - used by munin-config) -# -# todo: -# try using "graph_period" option "to make graph_sums usable" -# -# Magic markers - optional - used by installation scripts and -# munin-config: +# Usage: place in /etc/munin/plugins (or link it there using ln -s) # #%# family=contrib #%# capabilities=autoconf use strict; +use feature ':5.10'; use IO::Socket::INET; +use Munin::Plugin; # Config -our $address = $ENV{host} || "localhost"; # Default: localhost -our $port = $ENV{port} || 9051; # Default: 9051 +my $address = $ENV{host} || "localhost"; +my $port = $ENV{port} || 9051; # Don't edit below this line sub Authenticate { - my ($socket) = @_; - my $authline = "AUTHENTICATE"; - if (defined($ENV{cookiefile})) { - if (open(COOKIE, "<$ENV{cookiefile}")) { - binmode COOKIE; - my $cookie; - $authline .= " "; - while (read(COOKIE, $cookie, 32)) { - foreach my $byte (unpack "C*", $cookie) { - $authline .= sprintf "%02x", $byte; - } - } - close COOKIE; - } - } elsif (defined($ENV{password})) { - $authline .= ' "' . $ENV{password} . '"'; - } - print $socket "$authline\r\n"; - my $replyline = <$socket>; - if (substr($replyline, 0, 1) != '2') { - $replyline =~ s/\s*$//; - return "Failed to authenticate: $replyline"; - } + my ($socket) = @_; + my $authline = "AUTHENTICATE"; + if (defined($ENV{cookiefile})) { + if (open(COOKIE, "<$ENV{cookiefile}")) { + my $cookie; + binmode COOKIE; + read(COOKIE, $cookie, 32); + close COOKIE; + $authline .= ' "' . $cookie . '"'; + } + } elsif (defined($ENV{password})) { + $authline .= ' "' . $ENV{password} . '"'; + } + say $socket "$authline"; + my $replyline = <$socket>; + if (substr($replyline, 0, 1) != '2') { + $replyline =~ s/\s*$//; + return "Failed to authenticate: $replyline"; + } - return; + return; } if ($ARGV[0] and $ARGV[0] eq "autoconf") { - # Try to connect to the daemon - my $socket = IO::Socket::INET->new("$address:$port") - or my $failed = 1; + # Try to connect to the daemon + my $socket = IO::Socket::INET->new("$address:$port") or my $failed = 1; - if ($failed) { - print "no (failed to connect to $address port $port)\n"; - exit 1; - } + if ($failed) { + say "no (failed to connect to $address port $port)"; + exit 1; + } - my $msg = Authenticate($socket); - if (defined($msg)) { - print $socket "QUIT\r\n"; - close($socket); - print "no ($msg)\n"; - exit 1; - } + my $msg = Authenticate($socket); + if (defined($msg)) { + say $socket "QUIT"; + close($socket); + say "no ($msg)"; + exit 1; + } - print $socket "QUIT\r\n"; - close($socket); - print "yes\n"; - exit 0; + say $socket "QUIT"; + close($socket); + say "yes"; + exit 0; } if ($ARGV[0] and $ARGV[0] eq "config") { - print "graph_title Tor bandwidth usage (in/out)\n"; - print "graph_args --base 1000\n"; - print "graph_vlabel bytes/sec\n"; - print "graph_category Tor\n"; - print "graph_info This graph shows the flowing incoming/outgoing bytes on a Tor node\n"; - print "down.label Download\n"; - print "down.type DERIVE\n"; - print "down.min 0\n"; - print "up.label Upload\n"; - print "up.type DERIVE\n"; - print "up.min 0\n"; + say "graph_order down up"; + say "graph_title Tor traffic"; + say "graph_args --base 1000"; + say "graph_vlabel bits in (-) / out (+) per \${graph_period}"; + say "graph_category network"; + say "graph_info This graph shows the traffic through this Tor node."; + say "down.label received"; + say "down.type DERIVE"; + say 'down.graph no'; + say "down.cdef down,8,*"; + say "down.min 0"; + say "up.label b/s"; + say "up.type DERIVE"; + say "up.negative down"; + say "up.cdef up,8,*"; + say "up.min 0"; - exit 0; + exit 0; } my $socket = IO::Socket::INET->new("$address:$port") - or die("Couldn't connect to $address port $port: $!"); + or die("Couldn't connect to $address port $port: $!"); my $msg = Authenticate($socket); if (defined($msg)) { - print $socket "QUIT\r\n"; - close($socket); - die "$msg\n"; + say $socket "QUIT"; + close($socket); + die "$msg\n"; } -print $socket "GETINFO accounting/bytes\r\n"; +say $socket "GETINFO accounting/bytes"; my $down = 0; my $up = 0; my $replyline = <$socket>; chomp($replyline); -if ( $replyline =~ /^250-accounting\/bytes=(\d+)\s(\d+)\r$/ ) { - $down = $1; - $up = $2; +if ($replyline =~ /^250-accounting\/bytes=(\d+)\s(\d+)/) { + $down = $1; + $up = $2; } else { - die "Failed to get accounting info: $replyline\n"; + die "Failed to get accounting info: $replyline\n"; } -print $socket "QUIT\r\n"; +say $socket "QUIT"; close($socket); -print "down.value $down\n"; -print "up.value $up\n"; +say "down.value $down"; +say "up.value $up"; exit 0; From e215f1569918fab16701146d123b6e27424bac4c Mon Sep 17 00:00:00 2001 From: Lasse Karstensen Date: Sat, 4 Feb 2012 16:50:48 +0100 Subject: [PATCH 024/123] fix typos --- tools/munin-node-from-hell/README.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/munin-node-from-hell/README.rst b/tools/munin-node-from-hell/README.rst index 2e7a04aa..9cc61d74 100644 --- a/tools/munin-node-from-hell/README.rst +++ b/tools/munin-node-from-hell/README.rst @@ -11,7 +11,7 @@ Current features controlled via config file: * Respond slowly or never to queries. * Have plugins that always are in warning or critical. -* Extensive number of plugins runnint at once. +* Extensive number of plugins running at once. * Run on multiple ports at the same time, to test huge amounts of clients. @@ -21,8 +21,8 @@ Usage munin-node-from-hell takes two arguments; the mode and which config file to use. Mode is either --run or --muninconf. -This software is meant to run as an ordinary unix user, please don't run -it as root without some thought. +This software is meant to run as an ordinary Unix user, please don't run +it as root. You probably want: From 3b485e4546a650f0e2869a685177f43b45295081 Mon Sep 17 00:00:00 2001 From: Lasse Karstensen Date: Sat, 4 Feb 2012 18:56:29 +0100 Subject: [PATCH 025/123] moar plugins --- tools/munin-node-from-hell/tarpit.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/munin-node-from-hell/tarpit.conf b/tools/munin-node-from-hell/tarpit.conf index 3a0995de..ecc6cc29 100644 --- a/tools/munin-node-from-hell/tarpit.conf +++ b/tools/munin-node-from-hell/tarpit.conf @@ -6,7 +6,7 @@ pluginprofile = tarpit port = 3000 [pluginprofile:tarpit] -plugins = tarpit, load, locks +plugins = load, locks, tarpit, load, locks [pluginprofile:base] plugins = load, locks, locks, load, load, locks, locks, load, load, load From 9694ad80283ad10a16f24b41de94f3282b7e96b7 Mon Sep 17 00:00:00 2001 From: Alejandro Olivan Alvarez Date: Mon, 6 Feb 2012 16:36:30 +0100 Subject: [PATCH 026/123] - new plugin for shoutcast v2 --- plugins/other/shoutcast2 | 158 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100755 plugins/other/shoutcast2 diff --git a/plugins/other/shoutcast2 b/plugins/other/shoutcast2 new file mode 100755 index 00000000..5e0ed963 --- /dev/null +++ b/plugins/other/shoutcast2 @@ -0,0 +1,158 @@ +#!/usr/bin/php + (string)$xml->CURRENTLISTENERS, + 'PEAKLISTENERS' => (string)$xml->PEAKLISTENERS, + 'MAXLISTENERS' => (string)$xml->MAXLISTENERS, + 'UNIQUELISTENERS' => (string)$xml->UNIQUELISTENERS, + ); + + // this is the usual case, generating a label and value + echo("max_connections.value {$dnas_data['MAXLISTENERS']}\n"); + echo("ax_used_connections.value {$dnas_data['PEAKLISTENERS']}\n"); + echo("all_connections.value {$dnas_data['CURRENTLISTENERS']}\n"); + echo("unique_connections.value {$dnas_data['UNIQUELISTENERS']}\n"); + + +} +else +{ + $dnas_data = array('ERROR' => 'Could not connect to dnas-server!'); + echo("\n"); +} + + + +// this is for munin's configuration tool +// could do something more complicated here +if(count($argv) == 2 && $arv[1] == 'authconf') { + exit('yes'); +} + + + +// this is for rrdtool to know how to label the graph + + // Default Settings +if(count($argv) == 2 && $argv[1] == 'config') { + echo("graph_title SHOUTcast Online on GlobalMobile port 8026\n"); + echo("graph_args --base 1000\n"); + echo("graph_category shoutcast\n"); + echo("graph_vlabel Connections per \${graph_period}\n"); + + // Max Listeners Allowed to Connect to Server + echo("max_connections.draw AREA\n\n"); + echo("max_connections.colour cdcfc4\n"); + echo("max_connections.min 0\n"); + echo("max_connections.label Max Slots\n"); + echo("max_connections.type GAUGE\n"); + + // Peak Listeners + echo("ax_used_connections.draw AREA\n"); + echo("ax_used_connections.colour ffd660\n"); + echo("ax_used_connections.min 0\n"); + echo("ax_used_connections.label Peak Listeners\n"); + echo("ax_used_connections.type GAUGE\n"); + + // Max Listeners Connected to Server + echo("all_connections.draw LINE1\n"); + echo("all_connections.colour a00e95\n"); + echo("all_connections.min 0\n"); + echo("all_connections.label Listeners\n"); + echo("all_connections.type GAUGE\n"); + + // Max Unique Listeners Connected to Server + echo("unique_connections.draw LINE1\n"); + echo("unique_connections.colour 330099\n"); + echo("unique_connections.min 0\n"); + echo("unique_connections.label Unique Listeners\n"); + echo("unique_connections.type GAUGE\n"); + + exit(); + +} + +//closing the connection +curl_close($ch); + + +?> From b9f04c70a0c01f32f5896613e7347aa42be43b9b Mon Sep 17 00:00:00 2001 From: Steve Schnepp Date: Mon, 6 Feb 2012 18:18:40 +0100 Subject: [PATCH 027/123] - adding a CGI for a JSON-ified datafile --- tools/munin2json/cgi-bin/munin-cgi-datafile | 74 +++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 tools/munin2json/cgi-bin/munin-cgi-datafile diff --git a/tools/munin2json/cgi-bin/munin-cgi-datafile b/tools/munin2json/cgi-bin/munin-cgi-datafile new file mode 100644 index 00000000..497a7bb2 --- /dev/null +++ b/tools/munin2json/cgi-bin/munin-cgi-datafile @@ -0,0 +1,74 @@ +#! /usr/bin/perl + +use warnings; +use strict; + +use CGI; +use Scalar::Util qw(isweak); + +use JSON; +use Munin::Master::Utils; +use Munin::Common::Defaults; + +my $conffile = $Munin::Common::Defaults::MUNIN_CONFDIR . "/munin.conf"; +my $config = munin_config($conffile); + + +# Header +my $cgi = CGI->new(); +print $cgi->header( + -status => 200, + -type => "application/json", + -expires=>'+5m', +); + +# Body +my $json = JSON->new->allow_nonref; + +# Remove all recursive refs +remove_weak($config); + +print $json->pretty->encode($config); + +sub remove_weak { + my $cur = shift; + if (ref($cur) eq 'HASH') { + for my $key (keys %$cur) { + # Ignore scalars + next unless ref $cur->{$key}; + + my $to_remove = isweak $cur->{$key}; + $to_remove ||= ($key eq "#%#parent"); + $to_remove ||= ($key eq "#%#root"); + # Remove weak refs + if ($to_remove) { + delete $cur->{$key}; + next; + } + + # Not weak, go down. + remove_weak($cur->{$key}) if ref $cur->{$key}; + } + } elsif (ref($cur) eq 'ARRAY') { + for (my $i = 0 ; $i < @$cur ; $i++) { + # Ignore scalars + next unless ref $cur->[$i]; + + # Remove weak refs + if (isweak $cur->[$i]) { + delete $cur->[$i]; + next; + } + + # Not weak, go down. + remove_weak($cur->[$i]); + } + } elsif (ref($cur) eq 'SCALAR') { + # Ignore scalars + next unless ref $$cur; + + remove_weak($$cur); + } else { + # This cannot be circular, ignoring + } +} From 293ce084088f71f038dc4b06c17a3c1e1799f4aa Mon Sep 17 00:00:00 2001 From: Steve Schnepp Date: Mon, 6 Feb 2012 18:19:22 +0100 Subject: [PATCH 028/123] - chmod +x --- tools/munin2json/cgi-bin/munin-cgi-datafile | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 tools/munin2json/cgi-bin/munin-cgi-datafile diff --git a/tools/munin2json/cgi-bin/munin-cgi-datafile b/tools/munin2json/cgi-bin/munin-cgi-datafile old mode 100644 new mode 100755 From 3716785b5ce3575c40fa607260e57ba77937b756 Mon Sep 17 00:00:00 2001 From: Bjorn Ruberg Date: Tue, 7 Feb 2012 15:05:02 +0100 Subject: [PATCH 029/123] - Adding a new squeezebox plugin, multigraph-enabled --- plugins/other/squeezebox_multi | 226 +++++++++++++++++++++++++++++++++ 1 file changed, 226 insertions(+) create mode 100755 plugins/other/squeezebox_multi diff --git a/plugins/other/squeezebox_multi b/plugins/other/squeezebox_multi new file mode 100755 index 00000000..92ca0e61 --- /dev/null +++ b/plugins/other/squeezebox_multi @@ -0,0 +1,226 @@ +#!/usr/bin/perl + +=head1 NAME + +squeezebox_ - plugin to monitor a Logitech Media Server and associated +players. + +=head1 APPLICABLE SYSTEMS + +Probably any system running Logitech Media Server. Change the host to +enable remote monitoring. + +=head1 CONFIGURATION + +No configuration should be required if run on the same server as +the Logitech Media Server. If the plugin is run from another unit or +in a non-default configuration, please use the environment variables +'squeezebox_host' and 'squeezebox_port' to connect. + +Sample content for plugin-conf.d/ follows: + + [squeezebox] + env.squeezecenter_host 192.168.100.10 + env.squeezecenter_port 9095 + +=head1 INTERPRETATION + +The "volume" graphs only graphs the player volume levels, not the +amplifier or whatever the player may be connected to. + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf suggest + +=head1 BUGS + +None known + +=head1 AUTHOR + +Bjørn Ruberg + +=head1 LICENSE + +GPLv2 + +=cut + +#%# family=auto +#%# capabilities=autoconf suggest + +use strict; + +my $ret = undef; +if (! eval "require Net::Telnet;") { + $ret = "Net::Telnet not found"; +} +if (! eval "require URI::Escape;") { + $ret = "URI::Escape not found"; +} + +use vars qw ($host $port $config); + +# Define connection settings, from plugin environment file or use defaults +my $host = exists $ENV{'squeezecenter_host'} ? $ENV{'squeezecenter_host'} : "127.0.0.1"; +my $port = exists $ENV{'squeezecenter_port'} ? $ENV{'squeezecenter_port'} : "9090"; + +# Argument handling: autoconf, suggest, update, config + +if (@ARGV) { + if ($ARGV[0] eq "autoconf") { + # autoconf for magically self-configuring the plugin + if ($ret) { + print "no ($ret)"; + exit 0; + } + my $conn = new Net::Telnet (Telnetmode => 0); + $conn->open (Host => $host, + Port => $port, + Errmode => "return"); + if ($conn->errmsg) { + print "no (No connection on $host port $port)"; + exit 0; + } else { + my $version = ""; + my $line = ""; + $conn->print("version ?"); + $conn->print("exit"); + while (($line = $conn->getline) and ($line !~ /exit/)) { + $version = $line if $line =~ /version/; + } + if ($version =~ /^version/) { + print "yes"; + exit 0; + } else { + print "no (socket responded but the server didn't respond as expected)"; + exit 0; + } + } + } elsif ($ARGV[0] eq "suggest") { + print "I am a multigraph plugin, and suggest is not required\n"; + exit 0; + } elsif ($ARGV[0] eq "update") { + # For scheduled inventory rescan, add this plugin to a cron job + # with the argument "update" Adjust the interval to your own tempo + # for adding/deleting music. This equals a "Look for new and + # changed media files" rescan from the webUI. + # + # example: 5 * * * * /usr/share/munin/plugins/squeezebox update + + my $conn = new Net::Telnet (Telnetmode => 0); + $conn->open (Host => $host, + Port => $port, + Errmode => "return"); + if ($conn->errmsg) { + print $conn->errmsg, "\n";; + exit 1; + } else { + $conn->print("rescan"); + $conn->print("exit"); + exit 0; + } + } elsif ($ARGV[0] eq "config") { + # Sets $config value for using in the main execution cycle + $config = 1; + } +} + +# We're keeping the socket open for all checks +my $conn = new Net::Telnet (Telnetmode => 0); +$conn->open (Host => $host, + Port => $port); + +use URI::Escape; +# use Encode qw (from_to); +use Text::Iconv; +my $converter = Text::Iconv->new("UTF-8", "LATIN1"); +# $converted = $converter->convert("Text to convert"); + +# First all the simple readings +foreach my $attr qw (albums artists genres songs) { + $conn->print ("info total ${attr} ?"); + my $line = uri_unescape($conn->getline); + if ($line =~ /^info total ${attr} (\d+)$/) { + my $number = $1; + print "multigraph squeezebox_${attr}\n"; + if ($config) { + print "graph_title Number of ${attr}\n"; + print "graph_scale no\n"; + print "graph_category Squeezebox\n"; + print "${attr}.label ${attr}\n"; + } else { + print "${attr}.value $number\n"; + } + } +} + +# years +$conn->print ("years"); +if (uri_unescape($conn->getline) =~ /^years\s+count:(\d+)/) { + my $no_of_years = $1; + $conn->print ("years 0 $no_of_years"); + my @years = split (" ", uri_unescape($conn->getline)); + print "multigraph squeezebox_years\n"; + + if ($config) { + # config run + print "graph_title Albums per year\n"; + print "graph_category Squeezebox\n"; + print "graph_args --base 1000 -l 0\n"; + foreach my $year (@years) { + if ($year =~ /year\:(\d+)/) { + print "y" . $1 . ".label $1\n"; + print "y" . $1 . ".draw AREASTACK\n"; + } + } + } else { + # regular run + foreach my $year (@years) { + if ($year =~ /(year\:\d+)/) { + $conn->print ("albums 0 0 $1"); + # albums 0 0 year:2007 count:13 + my $line = uri_unescape ($conn->getline); + if ($line =~ /^.*year\:(\d+) count\:(\d+)$/) { + print "y${1}.value ${2}\n"; + } + + } + } + } +} + +# mixer volume and signalstrength +foreach my $attr ("signalstrength", "mixer volume") { + # The plugin reports as squeezebox_volume while the command is + # "mixer volume". + (my $attr_print = $attr) =~ s/mixer //g; + print "multigraph squeezebox_${attr_print}\n"; + if ($config) { + print "graph_title " . ucfirst ($attr) . "\n"; + print "graph_category Squeezebox\n"; + } + $conn->print ("player count ?"); + (my $no_of_players = uri_unescape ($conn->getline)) =~ s/player count\s+//; + chomp $no_of_players; + my $id; + for ($id = 0; $id < $no_of_players; $id++) { + $conn->print ("player id ${id} ?"); + (my $mac = uri_unescape ($conn->getline)) =~ s/player id \d+ //g; + chomp ($mac); + (my $mac_print = 'm' . $mac) =~ s/\://g; + if ($config) { + $conn->print ("player name ${mac} ?"); + (my $name = uri_unescape ($conn->getline)) =~ s/player name ${mac} //g; + chomp $name; + print "${mac_print}.label ", $converter->convert($name), "\n"; + } else { + $conn->print ("${mac} ${attr} ?"); + (my $value = uri_unescape ($conn->getline)) =~ s/^.* //g; + chomp $value; + print "${mac_print}.value $value\n"; + } + } +} + From 66012ed0be3f6f501c6d87c1805869d175b0ed55 Mon Sep 17 00:00:00 2001 From: Steve Schnepp Date: Wed, 8 Feb 2012 17:40:34 +0100 Subject: [PATCH 030/123] Typos (thx ssm) --- plugins/other/shoutcast2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/other/shoutcast2 b/plugins/other/shoutcast2 index 5e0ed963..250c7ebb 100755 --- a/plugins/other/shoutcast2 +++ b/plugins/other/shoutcast2 @@ -104,7 +104,7 @@ else // this is for munin's configuration tool // could do something more complicated here -if(count($argv) == 2 && $arv[1] == 'authconf') { +if(count($argv) == 2 && $argv[1] == 'autoconf') { exit('yes'); } From a2dd3b1b703ad6fb44f72ab1a2c470c4e7cd1d93 Mon Sep 17 00:00:00 2001 From: Matt West Date: Thu, 9 Feb 2012 03:45:43 -0800 Subject: [PATCH 031/123] Adding shoutcast2_multi plugin written in perl, should function on both 32bit and 64bit machines --- plugins/other/shoutcast2_multi | 331 +++++++++++++++++++++++++++++++++ 1 file changed, 331 insertions(+) create mode 100755 plugins/other/shoutcast2_multi diff --git a/plugins/other/shoutcast2_multi b/plugins/other/shoutcast2_multi new file mode 100755 index 00000000..e1202ad8 --- /dev/null +++ b/plugins/other/shoutcast2_multi @@ -0,0 +1,331 @@ +#!/usr/bin/perl +# +=head1 Shoutcast 2.0.x Plugin + + A Plugin for monitoring a Shoutcast 2.0.x Server (Multigraph) + +=head1 Munin Configuration + + [shoutcast2_multi] + env.host 127.0.0.1 *default* + env.port 8000 *default* + env.pass changeme *default* + +=head2 Munin Configuration Explanation + + host = host we are attempting to connection to, can be ip, or hostname + port = port we need to connect to in order to get to admin.cgi + pass = password to use to authenticate as admin user + +=head1 License + + GPLv2 + +=head1 Magic Markers + +#%# family=auto +#%# capabilities=autoconf + +=cut + +use strict; +use warnings; +use LWP::UserAgent; +use XML::Simple; +use Munin::Plugin; + +need_multigraph(); + +=head1 Variable Declarations + + This section is mainly for importing / declaring our environment variables. + This is were we will import the data from our plugin-conf.d file so we can + override the default settings which will only work for Shoutcast test configs. + +=cut + +my $host = $ENV{host} || '127.0.0.1'; +my $port = $ENV{port} || 8000; +my $pass = $ENV{pass} || 'changeme'; + +# Initialize hashref for storing results information... +my $dataRef; + +# Create a hashref for our graph information that we will call up later... +my $graphsRef; + +my $ua = LWP::UserAgent->new(); +$ua->agent('Munin Shoutcast Plugin/0.1'); +$ua->timeout(5); +$ua->credentials($host.':'.$port, 'Shoutcast Server', 'admin'=>$pass); + +=head1 Graphs Declarations + + The following section of code contains our graph information. This is the data + provided to Munin, so that it may properly draw our graphs based on the values + the plugin returns. + + While you are free to change colors or labels changing the type, min, or max + could cause unfortunate problems with your graphs. + +=cut + +$graphsRef->{active} = { + config => { + args => '--base 1000 --lower-limit 0', + vlabel => 'Is a DJ Actively Connected?', + category => 'shoutcast2', + title => 'Active States', + info => 'This graph shows us the active state or not, depending on if a DJ is connected', + }, + datasrc => [ + { name => 'active', draw => 'AREA', min => '0', max => '1', label => 'On or Off', type => 'GAUGE' }, + ], +}; + +$graphsRef->{listeners} = { + config => { + args => '--base 1000 --lower-limit 0', + vlabel => 'Listener Count', + category => 'shoutcast2', + title => 'Listeners', + info => 'This graph shows us the various counts for listener states we are tracking', + }, + datasrc => [ + { name => 'maxlisteners', draw => 'STACK', min => '0', label => 'Max Listeners', type => 'GAUGE' }, + { name => 'currlisteners', draw => 'AREA', min => '0', label => 'Current Listeners', type => 'GAUGE' }, + ], +}; + +$graphsRef->{sid_active} = { + config => { + args => '--base 1000 --lower-limit 0', + vlabel => 'Is a DJ Actively Connected?', + title => 'Active State', + info => 'This graph shows us the active state or not, depending on if a DJ is connected', + }, + datasrc => [ + { name => 'active', draw => 'AREA', min => '0', max => '1', label => 'On or Off', type => 'GAUGE', xmlkey => 'STREAMSTATUS' }, + ], +}; + +$graphsRef->{sid_listeners} = { + config => { + args => '--base 1000 --lower-limit 0', + vlabel => 'Listener Count', + title => 'Listeners', + info => 'This graph shows us the various counts for listener states we are tracking', + }, + datasrc => [ + { name => 'maxlisteners', draw => 'STACK', min => '0', label => 'Max Listeners', type => 'GAUGE', xmlkey => 'MAXLISTENERS' }, + { name => 'currlisteners', draw => 'AREA', min => '0', label => 'Current Listeners', type => 'GAUGE', xmlkey => 'CURRENTLISTENERS' }, + { name => 'peaklisteners', draw => 'LINE2', min => '0', label => 'Peak Listeners', type => 'GAUGE', xmlkey => 'PEAKLISTENERS' }, + { name => 'uniqlisteners', draw => 'LINE2', min => '0', label => 'Unique Listeners', type => 'GAUGE', xmlkey => 'UNIQUELISTENERS' }, + ], +}; + +if (defined($ARGV[0]) && ($ARGV[0] eq 'config')) { + config(); + exit; +} + +if (defined($ARGV[0]) && ($ARGV[0] eq 'autoconf')) { + check_autoconf(); + exit; +} + +# I guess we are collecting stats to return, execute main subroutine. +main(); + +exit; + +sub main { + my ($returnBit,$adminRef) = fetch_admin_data($ua,$host,$port); + if ($returnBit == 0) { + exit; + } + my $streamConfigRef = $adminRef->{STREAMCONFIGS}->{STREAMCONFIG}; + my $sidDataRef; + foreach my $sid (keys %{$streamConfigRef}) { + my ($return,$tmpSidRef) = fetch_sid_data($sid); + if ($return == 0) { + next; + } + $sidDataRef->{$sid} = $tmpSidRef; + } + print_active_data($sidDataRef); + print_listener_data($adminRef->{STREAMCONFIGS}->{SERVERMAXLISTENERS}, $sidDataRef); + return; +} + +sub print_active_data { + my ($sidDataRef) = (@_); + my $globalActive = 0; + foreach my $sid (sort keys %{$sidDataRef}) { + print "multigraph shoutcast2_active.active_sid_$sid\n"; + foreach my $dsrc (@{$graphsRef->{sid_active}->{datasrc}}) { + print "$dsrc->{name}.value $sidDataRef->{$sid}->{$dsrc->{xmlkey}}\n"; + if ($sidDataRef->{$sid}->{$dsrc->{xmlkey}} == 1) { + $globalActive = 1; + } + } + } + print "multigraph shoutcast2_active\n"; + foreach my $dsrc (@{$graphsRef->{active}->{datasrc}}) { + print "$dsrc->{name}.value $globalActive\n"; + } + return; +} + +sub print_listener_data { + my ($maxListeners,$sidDataRef) = (@_); + my $globalListeners = 0; + foreach my $sid (sort keys %{$sidDataRef}) { + print "multigraph shoutcast2_listeners.listeners_sid_$sid\n"; + foreach my $dsrc (@{$graphsRef->{sid_listeners}->{datasrc}}) { + print "$dsrc->{name}.value $sidDataRef->{$sid}->{$dsrc->{xmlkey}}\n"; + if ($dsrc->{name} eq 'currlisteners') { + $globalListeners += $sidDataRef->{$sid}->{$dsrc->{xmlkey}}; + } + } + } + print "multigraph shoutcast2_active\n"; + foreach my $dsrc (@{$graphsRef->{listeners}->{datasrc}}) { + if ($dsrc->{name} eq 'maxlisteners') { + print "$dsrc->{name}.value $maxListeners\n"; + } else { + print "$dsrc->{name}.value $globalListeners\n"; + } + } + return; +} + +sub config { + my ($returnBit,$adminRef) = fetch_admin_data($ua,$host,$port); + if ($returnBit == 0) { + # $adminRef returned a string, we'll just print it out. + print "no (error response: $adminRef)\n"; + exit; + } + my $streamConfigRef = $adminRef->{STREAMCONFIGS}->{STREAMCONFIG}; + my $sidDataRef; + foreach my $sid (keys %{$streamConfigRef}) { + my ($return,$tmpSidRef) = fetch_sid_data($sid); + if ($return == 0) { + next; + } + $sidDataRef->{$sid} = $tmpSidRef; + } + print_active_config($sidDataRef); + print_listener_config($sidDataRef); + return; +} + +sub print_active_config { + my ($sidDataRef) = (@_); + foreach my $sid (sort keys %{$sidDataRef}) { + # Print the Config Info First + print "multigraph shoutcast2_active.active\_sid\_$sid\n"; + print "graph_title ".$graphsRef->{sid_active}->{config}->{title}." for StreamID: $sid\n"; + print "graph_args ".$graphsRef->{sid_active}->{config}->{args}."\n"; + print "graph_vlabel ".$graphsRef->{sid_active}->{config}->{vlabel}."\n"; + print "graph_category streamid_$sid\n"; + print "graph_info ".$graphsRef->{sid_active}->{config}->{info}."\n"; + # Print the Data Value Info + foreach my $dsrc (@{$graphsRef->{sid_active}->{datasrc}}) { + while ( my ($key, $value) = each (%{$dsrc})) { + next if ($key eq 'name'); + next if ($key eq 'xmlkey'); + print "$dsrc->{name}.$key $value\n"; + } + } + } + print "multigraph shoutcast2_active\n"; + print "graph_title ".$graphsRef->{active}->{config}->{title}."\n"; + print "graph_args ".$graphsRef->{active}->{config}->{args}."\n"; + print "graph_vlabel ".$graphsRef->{active}->{config}->{vlabel}."\n"; + print "graph_category".$graphsRef->{active}->{config}->{category}."\n"; + print "graph_info ".$graphsRef->{active}->{config}->{info}."\n"; + # Print the Data Value Info + foreach my $dsrc (@{$graphsRef->{active}->{datasrc}}) { + while ( my ($key, $value) = each (%{$dsrc})) { + next if ($key eq 'name'); + print "$dsrc->{name}.$key $value\n"; + } + } + return; +} + +sub print_listener_config { + my ($sidDataRef) = (@_); + foreach my $sid (sort keys %{$sidDataRef}) { + # Print the Config Info First + print "multigraph shoutcast2_listeners.listeners\_sid\_$sid\n"; + print "graph_title ".$graphsRef->{sid_listeners}->{config}->{title}." for StreamID: $sid\n"; + print "graph_args ".$graphsRef->{sid_listeners}->{config}->{args}."\n"; + print "graph_vlabel ".$graphsRef->{sid_listeners}->{config}->{vlabel}."\n"; + print "graph_category streamid_$sid\n"; + print "graph_info ".$graphsRef->{sid_listeners}->{config}->{info}."\n"; + # Print the Data Value Info + foreach my $dsrc (@{$graphsRef->{sid_listeners}->{datasrc}}) { + while ( my ($key, $value) = each (%{$dsrc})) { + next if ($key eq 'name'); + next if ($key eq 'xmlkey'); + print "$dsrc->{name}.$key $value\n"; + } + } + } + print "multigraph shoutcast2_listeners\n"; + print "graph_title ".$graphsRef->{listeners}->{config}->{title}."\n"; + print "graph_args ".$graphsRef->{listeners}->{config}->{args}."\n"; + print "graph_vlabel ".$graphsRef->{listeners}->{config}->{vlabel}."\n"; + print "graph_category".$graphsRef->{listeners}->{config}->{category}."\n"; + print "graph_info ".$graphsRef->{listeners}->{config}->{info}."\n"; + # Print the Data Value Info + foreach my $dsrc (@{$graphsRef->{listeners}->{datasrc}}) { + while ( my ($key, $value) = each (%{$dsrc})) { + next if ($key eq 'name'); + print "$dsrc->{name}.$key $value\n"; + } + } + return; +} + +sub check_autoconf { + my ($returnBit,$adminRef) = fetch_admin_data($ua,$host,$port); + if ($returnBit == 0) { + # $adminRef returned a string, we'll just print it out. + print "no (error response: $adminRef)\n"; + } else { + print "yes\n"; + } + return; +} + +sub fetch_sid_data { + my ($sid) = (@_); + my $url = 'http://'.$host.':'.$port.'/stats?sid='.$sid; + my $response = $ua->get($url); + if ($response->is_success) { + my $returnRef = XMLin($response->decoded_content); + return (1, $returnRef); + } else { + return (0, $response->status_line); + } +} + +sub fetch_admin_data { + my $url = 'http://'.$host.':'.$port.'/admin.cgi?sid=1&mode=viewxml&page=6'; + my $response = $ua->get($url); + if ($response->is_success) { + my $returnRef = XMLin($response->decoded_content); + if (($returnRef->{STREAMCONFIGS}->{TOTALCONFIGS} > 0) && (defined($returnRef->{STREAMCONFIGS}->{STREAMCONFIG}))) { + return (1, $returnRef); + } else { + return (0, 'Unable to Detect any Stream Configurations'); + } + } else { + return (0, $response->status_line); + } +} + From 352a9baf9429ac320613b927e7205f5b747566b4 Mon Sep 17 00:00:00 2001 From: Lasse Karstensen Date: Thu, 9 Feb 2012 14:47:17 +0100 Subject: [PATCH 032/123] Add UTF8 plugins --- .../munin-node-from-hell/muninnode-from-hell | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tools/munin-node-from-hell/muninnode-from-hell b/tools/munin-node-from-hell/muninnode-from-hell index 06f0ef60..fabab78b 100755 --- a/tools/munin-node-from-hell/muninnode-from-hell +++ b/tools/munin-node-from-hell/muninnode-from-hell @@ -1,4 +1,5 @@ #!/usr/bin/python +# .- coding: utf-8 -. # # Artificial munin node that behaves in all the ways you would like # ordinary nodes _not_ to behave. @@ -135,6 +136,40 @@ locked.info The number of processes that have pages locked into memory (for real """ modules["graph_area"] = graph_area() +class utf8_graphcat(MuninPlugin): + "A plugin with a graph category which has UTF-8 in it" + def fetch(self, conf): + load = open("/proc/loadavg", "r").read() + load, rest = load.split(" ", 1) + load = float(load) + return "apples.value %.2f" % load + + def config(self, conf): + return """graph_title Example UTF-8 graph +graph_vlabel apples +graph_category foo™ +apples.label apples +graph_info Apples eaten +apples.info Apples eaten""" +modules["utf8_graphcat"] = utf8_graphcat() + +class utf8_graphname(MuninPlugin): + "A plugin with a UTF-8 name" + def fetch(self, conf): + load = open("/proc/loadavg", "r").read() + load, rest = load.split(" ", 1) + load = float(load) + return "apples.value %.2f" % load + + def config(self, conf): + return """graph_title Example UTF-8 graph +graph_vlabel apples +graph_category system +apples.label apples +graph_info Apples eaten +apples.info Apples eaten""" +modules["utf8_™graphname"] = utf8_graphname() + class ArgumentTCPserver(SocketServer.ThreadingTCPServer): def __init__(self, server_address, RequestHandlerClass, args): From f110d3ddb1d146595dd4f7b1622b22c9b8ef1405 Mon Sep 17 00:00:00 2001 From: Lasse Karstensen Date: Thu, 9 Feb 2012 14:47:30 +0100 Subject: [PATCH 033/123] verbose mode as runtime flag --- tools/munin-node-from-hell/muninnode-from-hell | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/munin-node-from-hell/muninnode-from-hell b/tools/munin-node-from-hell/muninnode-from-hell index fabab78b..2256dbf6 100755 --- a/tools/munin-node-from-hell/muninnode-from-hell +++ b/tools/munin-node-from-hell/muninnode-from-hell @@ -188,7 +188,7 @@ class MuninHandler(SocketServer.StreamRequestHandler): """ def handle(self): - print "%s: Connection from %s:%s. server args is %s" \ + if self.server.args.get("verbose"): print "%s: Connection from %s:%s. server args is %s" \ % (self.server.args["name"], self.client_address[0], self.client_address[1], self.server.args) # slow path hostname = self.server.args["name"] @@ -264,7 +264,7 @@ def start_servers(instances): def usage(): - print "Usage: %s [--run] [--muninconf] " % sys.argv[0] + print "Usage: %s [--run] [--verbose] [--muninconf] " % sys.argv[0] def main(): if len(sys.argv) <= 2: @@ -326,6 +326,8 @@ def main(): instanceconfig[k] = v instanceconfig["plugins"] = plugins + if "--verbose" in sys.argv: + instanceconfig["verbose"] = True instanceconfig["name"] = "%s-%s" % (instancename, portinstance) instanceconfig["expanded_port"] = portinstance From 0442899e04cea0d35710a54444809f0a76f6cd0b Mon Sep 17 00:00:00 2001 From: Lasse Karstensen Date: Thu, 9 Feb 2012 15:42:42 +0100 Subject: [PATCH 034/123] last arg is the config file --- tools/munin-node-from-hell/muninnode-from-hell | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/munin-node-from-hell/muninnode-from-hell b/tools/munin-node-from-hell/muninnode-from-hell index 2256dbf6..30b05473 100755 --- a/tools/munin-node-from-hell/muninnode-from-hell +++ b/tools/munin-node-from-hell/muninnode-from-hell @@ -271,8 +271,12 @@ def main(): usage() sys.exit(1) + verbose = False + if "--verbose" in sys.argv: + verbose = True + config = ConfigParser.RawConfigParser() - config.read(sys.argv[2]) + config.read(sys.argv[-1]) instancekeys = [ key for key in config.sections() if key.startswith("instance:") ] servers = {} @@ -343,6 +347,7 @@ def main(): if "--run" in sys.argv: + if verbose: print "Starting up.." servers = start_servers(instances) try: From 957d093144dbe5d1950655a01d3a20126315c35c Mon Sep 17 00:00:00 2001 From: Steve Schnepp Date: Thu, 9 Feb 2012 18:30:37 +0800 Subject: [PATCH 035/123] Added some infos on the way the contrib repo works --- README.md | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 960c5caa..9907bc0f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,27 @@ This is the repository for all user contributed stuff -* contrib/tools/ is for 3rd-party tools. It usually serves as an incubator of trunk/contrib. -* contrib/plugins/ is for 3rd-party plugins. It will serve as the backend of exchange.munin-monitoring.org when it is operational again. -* contrib/templates/ is for 3rd-party templates. Feel free to update templates, or even to create new ones. Bonus points for mobile-friendly ones :) +# contrib/plugins/ - 3rd-party plugins + +**This is usually where you want to begin your journey.** + +Here you'll find all the plugins coming from http://exchange.munin-monitoring.org/. +It as evolved since then, but + +# contrib/templates/ - 3rd-party templates + +Feel free to update templates here, or even to create new ones. + +Bonus points for mobile-friendly ones :) + +Note that the one named `official` is a loose-synced copy of the one in SVN trunk. +It should serves as a base for small editions that can be resynced in SVN trunk, so for that : + +* don't copy the whole template +* directly edit files in this directory + +# contrib/tools/ - 3rd-party tools + +Here, you can put just any kind of tool. Please use this directory instead of a random place on the internet. +It makes things way more easy to search for others. + +And, it serves as an incubator of SVN `trunk/contrib` :-) From a247c870d75e66be3a0f8264f2253f0ad4862fec Mon Sep 17 00:00:00 2001 From: Paul Saunders Date: Mon, 16 Jan 2012 21:33:32 +0800 Subject: [PATCH 036/123] ejabberd_: Remove $MUNIN_DEBUG Of course, in bash the local $MUNIN_DEBUG is the same as the environment's $MUNIN_DEBUG. --- plugins/other/ejabberd_ | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/other/ejabberd_ b/plugins/other/ejabberd_ index 0fee887e..b7757d42 100755 --- a/plugins/other/ejabberd_ +++ b/plugins/other/ejabberd_ @@ -43,7 +43,6 @@ shopt -s extglob EJCTL=$(which ejabberdctl) EJVER=$($EJCTL status | awk '/^ejabberd / {print $2}') -MUNIN_DEBUG=${MUNIN_DEBUG:-0} if [ "$1" == "autoconf" ]; then if [ -x $EJCTL > /dev/null ]; then From b75bbfea929d05e2405cc9408f3895ea221ed9b4 Mon Sep 17 00:00:00 2001 From: Fyodor Yarochkin Date: Mon, 16 Jan 2012 23:53:32 +0800 Subject: [PATCH 037/123] typo in the doc :) --- plugins/other/riak_memory | 2 +- plugins/other/riak_node | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/other/riak_memory b/plugins/other/riak_memory index 25fe4764..c1bf08b5 100755 --- a/plugins/other/riak_memory +++ b/plugins/other/riak_memory @@ -5,7 +5,7 @@ # sample config in /etc/munin/plugin-conf.d/riak # # [riak_*] -# RIAK_URL=http://127.0.0.1:8091/stats +# env.RIAK_URL http://127.0.0.1:8091/stats # any questions to fygrave at o0o dot nu # # This plugin monitors memory allocation on each node. diff --git a/plugins/other/riak_node b/plugins/other/riak_node index 0ac851f8..1d3dcc89 100755 --- a/plugins/other/riak_node +++ b/plugins/other/riak_node @@ -4,7 +4,7 @@ # sample config in /etc/munin/plugin-conf.d/riak # # [riak_*] -# RIAK_URL=http://127.0.0.1:8091/stats +# env.RIAK_URL http://127.0.0.1:8091/stats # any questions to fygrave at o0o dot nu # # This plugin monitors put/get rate at each node. From 208bfbedb3e8a1aedf73e08ed62b6c8ac14a6464 Mon Sep 17 00:00:00 2001 From: Matt West Date: Thu, 9 Feb 2012 10:03:20 -0800 Subject: [PATCH 038/123] Updating Memcached Multigraph plugin to latest version --- ...{memcached-multigraph => memcached_multi_} | 468 +++++++++++------- 1 file changed, 289 insertions(+), 179 deletions(-) rename plugins/other/{memcached-multigraph => memcached_multi_} (67%) diff --git a/plugins/other/memcached-multigraph b/plugins/other/memcached_multi_ similarity index 67% rename from plugins/other/memcached-multigraph rename to plugins/other/memcached_multi_ index eac6a90c..e574861f 100755 --- a/plugins/other/memcached-multigraph +++ b/plugins/other/memcached_multi_ @@ -1,6 +1,6 @@ #!/usr/bin/perl # -=head1 NAME +=head1 MEMCACHED Memcached - A Plugin to monitor Memcached Servers (Multigraph) @@ -36,9 +36,6 @@ items => I This graphs the current items and total items in the memc memory => I This graphs the current and max memory allocation B -The following example holds true for all graphing options in this plugin. - Example: ln -s /usr/share/munin/plugins/memcached_multi_ /etc/munin/plugins/memcached_multi_bytes - =head1 ADDITIONAL INFORMATION You will find that some of the graphs have LEI on them. This was done in order to save room @@ -52,13 +49,11 @@ The B variable formats certain graphs based on the following guidelin =head1 ACKNOWLEDGEMENTS -The core of this plugin is based on the mysql_ plugin maintained by Kjell-Magne Ãierud - -Thanks to dormando as well for putting up with me ;) +Thanks to dormando for putting up with me ;) =head1 AUTHOR -Matt West < https://code.google.com/p/memcached-munin-plugin/ > +Matt West < https://github.com/mhwest13/Memcached-Munin-Plugin > =head1 LICENSE @@ -72,54 +67,77 @@ GPLv2 =cut use strict; +use warnings; use IO::Socket; use Munin::Plugin; +use File::Basename; +if (basename($0) !~ /^memcached_multi_/) { + print "This script needs to be named memcached_multi_ and have symlinks which start the same.\n"; + exit 1; +} + +# tell munin about our multigraph capabilities need_multigraph(); +=head1 Variable Declarations + + This section of code is to declare the variables used throughout the plugin + Some of them are imported as environment variables from munin plugin conf.d + file, others are hashes used for storing information that comes from the + stats commands issued to memcached. + +=cut + +# lets import environment variables for the plugin or use the default my $host = $ENV{host} || "127.0.0.1"; my $port = $ENV{port} || 11211; -my %stats; -# This hash contains the information contained in two memcache commands -# stats and stats settings. - -my %items; -# This gives us eviction rates and other hit stats per slab -# We track this so we can see if something was evicted earlier than necessary - -my %chnks; -# This gives us the memory size and usage per slab -# We track this so we can see what slab is being used the most and has no free chunks -# so we can re-tune memcached to allocate more pages for the specified chunk size - -my $timescale = $ENV{timescale} || 3; # This gives us the ability to control the timescale our graphs are displaying. # The default it set to divide by hours, if you want to get seconds set it to 1. # Options: 1 = seconds, 2 = minutes, 3 = hours, 4 = days +my $timescale = $ENV{timescale} || 3; + +# This hash contains the information contained in two memcache commands +# stats and stats settings. +my %stats; + +# This gives us eviction rates and other hit stats per slab +# We track this so we can see if something was evicted earlier than necessary +my %items; + +# This gives us the memory size and usage per slab +# We track this so we can see what slab is being used the most and has no free chunks +# so we can re-tune memcached to allocate more pages for the specified chunk size +my %chnks; + +=head2 Graph Declarations + + This block of code builds up all of the graph info for all root / sub graphs. + + %graphs: is a container for all of the graph definition information. In here is where you'll + find the configuration information for munin's graphing procedure. + Format: + + $graph{graph_name} => { + config => { + You'll find the main graph config stored here + { key => value }, + { ... }, + }, + datasrc => [ + Name: name given to data value + Attr: Attribute and value, attribute must be valid plugin argument + { name => 'Name', info => 'info about graph', ... }, + { ... }, + ], + } + +=cut -# So I was trying to figure out how to build this up, and looking at some good examples -# I decided to use the format, or for the most part, the format from the mysql_ munin plugin -# for Innodb by Kjell-Magne Ãierud, it just spoke ease of flexibility especially with multigraphs -# thanks btw ;) -# -# %graphs is a container for all of the graph definition information. In here is where you'll -# find the configuration information for munin's graphing procedure. -# Format: -# -# $graph{graph_name} => { -# config => { -# # You'll find keys and values stored here for graph manipulation -# }, -# datasrc => [ -# # Name: name given to data value -# # Attr: Attribute for given value -# { name => 'Name', (Attr) }, -# { ... }, -# ], -# } my %graphs; +# main graph for memcached item count $graphs{items} = { config => { args => '--base 1000 --lower-limit 0', @@ -133,7 +151,7 @@ $graphs{items} = { { name => 'total_items', label => 'New Items', min => '0', type => 'DERIVE' }, ], }; - +# main graph for memcached memory usage $graphs{memory} = { config => { args => '--base 1024 --lower-limit 0', @@ -147,7 +165,7 @@ $graphs{memory} = { { name => 'bytes', draw => 'AREA', label => 'Current Bytes Used', min => '0' }, ], }; - +# main graph for memcached network usage $graphs{bytes} = { config => { args => '--base 1000', @@ -162,7 +180,7 @@ $graphs{bytes} = { { name => 'bytes_written', type => 'DERIVE', label => 'Traffic in (-) / out (+)', negative => 'bytes_read', cdef => 'bytes_written,8,*', min => '0' }, ], }; - +# graph for memcached connections $graphs{conns} = { config => { args => '--base 1000 --lower-limit 0', @@ -178,7 +196,7 @@ $graphs{conns} = { { name => 'avg_conns' , label => 'Avg Connections', min => '0' }, ], }; - +# main graph for memcached commands issued $graphs{commands} = { config => { args => '--base 1000 --lower-limit 0', @@ -200,7 +218,7 @@ $graphs{commands} = { { name => 'decr_misses', type => 'DERIVE', label => 'Decrement Misses', info => 'Number of unsuccessful decrement requests', min => '0' }, ], }; - +# main graph for memcached eviction rates $graphs{evictions} = { config => { args => '--base 1000 --lower-limit 0', @@ -215,7 +233,7 @@ $graphs{evictions} = { { name => 'reclaimed', label => 'Reclaimed Items', info => 'Cumulative Reclaimed Item Entries Across All Slabs', type => 'DERIVE', min => '0' }, ], }; - +# sub graph for breaking memory info down by slab ( sub graph of memory ) $graphs{slabchnks} = { config => { args => '--base 1000 --lower-limit 0', @@ -230,7 +248,7 @@ $graphs{slabchnks} = { { name => 'free_chunks', label => 'Total Chunks Not in Use (Free)', min => '0' }, ], }; - +# sub graph for breaking commands down by slab ( sub graph of commands ) $graphs{slabhits} = { config => { args => '--base 1000 --lower-limit 0', @@ -247,7 +265,7 @@ $graphs{slabhits} = { { name => 'decr_hits', label => 'Decrement Requests', type => 'DERIVE', min => '0' }, ], }; - +# sub graph for breaking evictions down by slab ( sub graph of evictions ) $graphs{slabevics} = { config => { args => '--base 1000 --lower-limit 0', @@ -262,7 +280,7 @@ $graphs{slabevics} = { { name => 'reclaimed', label => 'Reclaimed Expired Items', info => 'This is number of times items were stored in expired entry memory space', type => 'DERIVE', min => '0' }, ], }; - +# sub graph for showing the time between an item was last evicted and requested ( sub graph of evictions ) $graphs{slabevictime} = { config => { args => '--base 1000 --lower-limit 0', @@ -275,7 +293,7 @@ $graphs{slabevictime} = { { name => 'evicted_time', label => 'Eviction Time (LEI)', info => 'Time Since Request for Last Evicted Item', min => '0' }, ], }; - +# sub graph for breaking items down by slab ( sub graph of items ) $graphs{slabitems} = { config => { args => '--base 1000 --lower-limit 0', @@ -288,7 +306,7 @@ $graphs{slabitems} = { { name => 'number', label => 'Items', info => 'This is the amount of items stored in this slab', min => '0' }, ], }; - +# sub graph for showing the age of the eldest item stored in a slab ( sub graph of items ) $graphs{slabitemtime} = { config => { args => '--base 1000 --lower-limit 0', @@ -302,37 +320,47 @@ $graphs{slabitemtime} = { ], }; -## -#### Config Check #### -## +=head1 Munin Checks + + These checks look for config / autoconf / suggest params + +=head2 Config Check + + This block of code looks at the argument that is possibly supplied, + should it be config, it then checks to make sure the plugin + specified exists, assuming it does, it will run the do_config + subroutine for the plugin specified, otherwise it dies complaining + about an unknown plugin. + +=cut if (defined $ARGV[0] && $ARGV[0] eq 'config') { - + # Lets get our plugin from the symlink being called up, we'll also verify its a valid + # plugin that we have graph information for $0 =~ /memcached_multi_(.+)*/; my $plugin = $1; - die 'Unknown Plugin Specified: ' . ($plugin ? $plugin : '') unless $graphs{$plugin}; - # We need to fetch the stats before we do any config, cause its needed for multigraph + # subgraphs which use slab information for title / info per slab fetch_stats(); - # Now lets go ahead and print out our config. do_config($plugin); exit 0; } -## -#### Autoconf Check #### -## +=head2 Autoconf Check + + This block of code looks at the argument that is possibly supplied, + should it be autoconf, we will attempt to connect to the memcached + service specified on the host:port, upon successful connection it + prints yes, otherwise it prints no. + +=cut if (defined $ARGV[0] && $ARGV[0] eq 'autoconf') { - - my $s = IO::Socket::INET->new( - Proto => "tcp", - PeerAddr => $host, - PeerPort => $port, - ); - + # Lets attempt to connect to memcached + my $s = get_conn(); + # Lets verify that we did connect to memcached if (defined($s)) { print "yes\n"; exit 0; @@ -342,18 +370,21 @@ if (defined $ARGV[0] && $ARGV[0] eq 'autoconf') { } } -## -#### Suggest Check #### -## +=head2 Suggest Check + + This block of code looks at the argument that is possibly supplied, + should it be suggest, we are going to print the possible plugins + which can be specified. Note we only specify the root graphs for the + multigraphs, since the rest of the subgraphs will appear "behind" the + root graphs. It also attempts to connect to the memcached service to + verify it is infact running. + +=cut if (defined $ARGV[0] && $ARGV[0] eq 'suggest') { - - my $s = IO::Socket::INET->new( - Proto => "tcp", - PeerAddr => $host, - PeerPort => $port, - ); - + # Lets attempt to connect to memcached + my $s = get_conn(); + # Lets check that we did connect to memcached if (defined($s)) { my @rootplugins = ('bytes','conns','commands','evictions','items','memory'); foreach my $plugin (@rootplugins) { @@ -366,30 +397,29 @@ if (defined $ARGV[0] && $ARGV[0] eq 'suggest') { } } -## -#### Well We aren't running (auto)config/suggest so lets print some stats #### -## +=head1 Output Subroutines + Output Subroutine calls to output data values + +=head2 fetch_output + + This subroutine is the main call for printing data for the plugin. + No parameters are taken as this is the default call if no arguments + are supplied from the command line. + +=cut + +# Well, no arguments were supplied that we know about, so lets print some data fetch_output(); -## -#### Subroutines for printing info gathered from memcached #### -## - -## -#### This subroutine performs the bulk processing for printing statistics. -## - sub fetch_output { - + # Lets get our plugin from the symlink being called up, we'll also verify its a valid + # plugin that we have graph information for $0 =~ /memcached_multi_(.+)*/; my $plugin = $1; - die 'Unknown Plugin Specified: ' . ($plugin ? $plugin : '') unless $graphs{$plugin}; - # Well we need to actually fetch the stats before we do anything to them. fetch_stats(); - # Now lets go ahead and print out our output. my @subgraphs; if ($plugin eq 'memory') { @@ -423,17 +453,24 @@ sub fetch_output { return; } -## -#### This subroutine is for the root non-multigraph graphs which render on the main node page #### -## +=head2 print_root_output + + This block of code prints out the return values for our root graphs. It takes + one parameter $plugin. Returns when completed, this is the non multigraph + output subroutine. + + $plugin; graph we are calling up to print data values for + + Example: print_root_output($plugin); + +=cut sub print_root_output { + # Lets get our plugin, set our graph reference and print out info for Munin my ($plugin) = (@_); - my $graph = $graphs{$plugin}; - print "graph memcached_$plugin\n"; - + # The conns plugin has some specific needs, looking for plugin type if ($plugin ne 'conns') { foreach my $dsrc (@{$graph->{datasrc}}) { my %datasrc = %$dsrc; @@ -461,26 +498,33 @@ sub print_root_output { } } } - return; } -## -#### This subroutine is for the root multigraph graphs which render on the main node page #### -## +=head2 print_rootmulti_output + + This block of code prints out the return values for our root graphs. It takes + one parameter $plugin. Returns when completed, this is the multigraph + output subroutine + + $plugin; main(root) graph we are calling up to print data values for + + Example: print_rootmulti_output($plugin); + +=cut sub print_rootmulti_output { + # Lets get our plugin, set our graph reference and print out info for Munin my ($plugin) = (@_); - my $graph = $graphs{$plugin}; - print "multigraph memcached_$plugin\n"; - + # Lets print our data values with their appropriate name foreach my $dsrc (@{$graph->{datasrc}}) { my $output = 0; my %datasrc = %$dsrc; while ( my ($key, $value) = each(%datasrc)) { next if ($key ne 'name'); + next if (($plugin eq 'evictions') && ($value eq 'reclaimed') && ($stats{version} =~ /1\.4\.[0-2]/)); if (($plugin eq 'evictions') && ($value eq 'evicted_nonzero')) { foreach my $slabid (sort{$a <=> $b} keys %items) { $output += $items{$slabid}->{evicted_nonzero}; @@ -491,24 +535,33 @@ sub print_rootmulti_output { print "$dsrc->{name}.value $output\n"; } } - return; } -## -#### This subroutine is for the sub multigraph graphs created via the multigraph plugin #### -## +=head2 print_submulti_output + + This block of code prints out the return values for our root graphs. It takes + three parameters $slabid, $plugin and @subgraphs. Returns when completed, this + is the multigraph output subroutine for our subgraphs + + $slabid; slab id that we will use to grab info from and print out + $plugin; main(root) being called, used for multigraph output and slab id + @subgraphs; graphs we are actually trying to print data values for + + Example: print_submulti_output($slabid,$plugin,@subgraphs); + +=cut sub print_submulti_output { + # Lets get our slabid, plugin, and subgraphs my ($slabid,$plugin,@subgraphs) = (@_); my $currslab = undef; - + # Time to loop over our subgraphs array foreach my $sgraph (@subgraphs) { - + # Lets set our graph reference for quick calling, and print some info for munin my $graph = $graphs{$sgraph}; - print "multigraph memcached_$plugin.$sgraph\_$slabid\n"; - + # Lets figure out what slab info we are trying to call up if ($plugin eq 'evictions') { $currslab = $items{$slabid}; } elsif ($plugin eq 'memory') { @@ -520,11 +573,12 @@ sub print_submulti_output { } else { return; } - + # Lets print our data values with their appropriate name foreach my $dsrc (@{$graph->{datasrc}}) { my %datasrc = %$dsrc; while ( my ($key, $value) = each(%datasrc)) { next if ($key ne 'name'); + next if (($sgraph eq 'slabevics') && ($value eq 'reclaimed') && ($stats{version} =~ /1\.4\.[0-2]/)); my $output = $currslab->{$value}; if (($sgraph eq 'slabevictime') || ($sgraph eq 'slabitemtime')) { $output = time_scale('data',$output); ; @@ -533,17 +587,23 @@ sub print_submulti_output { } } } - return; } -## -#### Subroutines for printing out config information for graphs #### -## +=head1 Config Subroutines -## -#### This subroutine does the bulk printing the config info per graph #### -## + These subroutines handle the config portion of munin calls. + +=head2 do_config + + This is the main call issued assuming we call up config and plugin specified exists + The subroutine takes one parameter $plugin, and returns when completed. + + $plugin; main(root) graph being called + + Example: do_config($plugin); + +=cut sub do_config { my ($plugin) = (@_); @@ -579,22 +639,59 @@ sub do_config { return; } -## -#### This subroutine is for the config info for sub multigraph graphs created via the multigraph plugin #### -## +=head2 get_conn + + This subroutine returns a socket connection + +=cut + +sub get_conn { + my $s = undef; + + # check if we want to use sockets instead of tcp + my ($sock) = ($host =~ /unix:\/\/(.+)*$/); + + if ($sock) { + $s = IO::Socket::UNIX->new( + Peer => $sock + ); + } else { + $s = IO::Socket::INET->new( + Proto => "tcp", + PeerAddr => $host, + PeerPort => $port, + Timeout => 10, + ); + } + + return $s; +} + +=head2 print_submulti_config + + This subroutine prints out the config information for all of the subgraphs. + It takes three parameters, $slabid, $plugin and @subgraphs, returns when + completed, this is the mutligraph config output for our subgraphs + + $slabid; slab id that we will use to grab info from and print out + $plugin; main(root) being called, used for multigraph output and slab id + @subgraphs; graphs we are actually trying to print data values for + + Example: print_submulti_config($slabid,$plugin,@subgraphs); + +=cut sub print_submulti_config { + # Lets get our slabid, plugin, and subgraphs my ($slabid,$plugin,@subgraphs) = (@_); my ($slabitems,$slabchnks) = undef; - + # Time to loop over our subgraphs array foreach my $sgraph (@subgraphs) { - + # Lets set our graph reference, and main graph config for easy handling my $graph = $graphs{$sgraph}; - my %graphconf = %{$graph->{config}}; - + # Lets tell munin which graph we are graphing, and what our main graph config info is print "multigraph memcached_$plugin.$sgraph\_$slabid\n"; - while ( my ($key, $value) = each(%graphconf)) { if ($key eq 'title') { print "graph_$key $value" . "$slabid" . " ($chnks{$slabid}->{chunk_size} Bytes)\n"; @@ -605,7 +702,7 @@ sub print_submulti_config { print "graph_$key $value\n"; } } - + # Lets tell munin about our data values and how to treat them foreach my $dsrc (@{$graph->{datasrc}}) { my %datasrc = %$dsrc; while ( my ($key, $value) = each(%datasrc)) { @@ -618,25 +715,28 @@ sub print_submulti_config { return; } -## -#### This subroutine is for the config info for root multigraph graphs which render on the main node page #### -## +=head2 print_rootmulti_config + + This subroutine prints out the config information for all of the main(root) graphs. + It takes one parameter, $plugin, returns when completed. + + $plugin; main(root) graph used for multigraph call + + Example: print_rootmulti_config($plugin); + +=cut sub print_rootmulti_config { + # Lets get out plugin, set our graph reference and our graph config info my ($plugin) = (@_); - - die 'Unknown Plugin Specified: ' . ($plugin ? $plugin : '') unless $graphs{$plugin}; - my $graph = $graphs{$plugin}; - my %graphconf = %{$graph->{config}}; - + # Lets tell munin about the graph we are referencing and print the main config print "multigraph memcached_$plugin\n"; - while ( my ($key, $value) = each(%graphconf)) { print "graph_$key $value\n"; } - + # Lets tell munin about our data values and how to treat them foreach my $dsrc (@{$graph->{datasrc}}) { my %datasrc = %$dsrc; while ( my ($key, $value) = each(%datasrc)) { @@ -644,29 +744,31 @@ sub print_rootmulti_config { print "$dsrc->{name}.$key $value\n"; } } - return; } -## -#### This subroutine is for the config info for non multigraph graphs which render on the main node page #### -## +=head2 print_root_config + + This subroutine prints out the config information for all of the main(root) graphs. + It takes one parameter, $plugin, returns when completed. + + $plugin; main(root) graph used for multigraph call + + Example: print_root_config($plugin); + +=cut sub print_root_config { + # Lets get our plugin, set our graph reference and our graph config info my ($plugin) = (@_); - - die 'Unknown Plugin Specified: ' . ($plugin ? $plugin : '') unless $graphs{$plugin}; - my $graph = $graphs{$plugin}; - my %graphconf = %{$graph->{config}}; - + # Lets tell munin about the graph we are referencing and print the main config print "graph memcached_$plugin\n"; - while ( my ($key, $value) = each(%graphconf)) { print "graph_$key $value\n"; } - + # Lets tell munin about our data values and how to treat them foreach my $dsrc (@{$graph->{datasrc}}) { my %datasrc = %$dsrc; while ( my ($key, $value) = each(%datasrc)) { @@ -674,46 +776,46 @@ sub print_root_config { print "$dsrc->{name}.$key $value\n"; } } - return; } -## -#### This subroutine actually performs the data fetch for us #### -#### These commands do not lock up Memcache at all #### -## +=head1 Misc Subroutines + + These subroutines are misc ones, and are referenced inside of the code. They + should never be called up by Munin. + +=head2 fetch_stats + + This subroutine fetches the information from memcached and stores it into our + hashes for later referencing throughout the graph. Returns when completed + +=cut sub fetch_stats { - my $s = IO::Socket::INET->new( - Proto => "tcp", - PeerAddr => $host, - PeerPort => $port, - ); - + # Lets try and connect to memcached + my $s = get_conn(); + # Die if we can't establish a connection to memcached die "Error: Unable to Connect to $host\[:$port\]\n" unless $s; - + # Lets print the stats command and store the info from the output print $s "stats\r\n"; - while (my $line = <$s>) { - if ($line =~ /STAT\s(.+?)\s(\d+)/) { + if ($line =~ /STAT\s(.+?)\s(.*)/) { my ($skey,$svalue) = ($1,$2); $stats{$skey} = $svalue; } last if $line =~ /^END/; } - + # Lets print the stats settings command and store the info from the output print $s "stats settings\r\n"; - while (my $line = <$s>) { - if ($line =~ /STAT\s(.+?)\s(\d+)/) { + if ($line =~ /STAT\s(.+?)\s(.*)/) { my ($skey,$svalue) = ($1,$2); $stats{$skey} = $svalue; } last if $line =~ /^END/; } - + # Lets print the stats slabs command and store the info from the output print $s "stats slabs\r\n"; - while (my $line = <$s>) { if ($line =~ /STAT\s(\d+):(.+)\s(\d+)/) { my ($slabid,$slabkey,$slabvalue) = ($1,$2,$3); @@ -721,9 +823,8 @@ sub fetch_stats { } last if $line =~ /^END/; } - + # Lets print the stats items command and store the info from the output print $s "stats items\r\n"; - while (my $line = <$s>) { if ($line =~ /STAT\sitems:(\d+):(.+?)\s(\d+)/) { my ($itemid,$itemkey,$itemvalue) = ($1,$2,$3); @@ -733,14 +834,23 @@ sub fetch_stats { } } -## -#### This subroutine is to help manage the time_scale settings for the graph -## +=head2 time_scale + + This subroutine is here for me to adjust the timescale of the time graphs + for last evicted item and age of eldest item in cache. + + Please note, after long usage I have noticed these counters may not + be accurate, I believe the developers are aware and have submitted + a patch upstream. + +=cut sub time_scale { + # Lets get our config option and value to adjust my ($configopt,$origvalue) = (@_); my $value; - + # If config is defined, it returns the config info for time scale + # If data is defined, it returns the original value after its been adjusted if ($configopt eq 'config') { if ($timescale == 1) { $value = "Seconds" . $origvalue; From 8be793e8224a5495bc02a7d04b1ecdef4266a905 Mon Sep 17 00:00:00 2001 From: Matt West Date: Thu, 9 Feb 2012 15:05:03 -0800 Subject: [PATCH 039/123] Fixing shoutcast2_multi plugin --- plugins/other/shoutcast2_multi | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/plugins/other/shoutcast2_multi b/plugins/other/shoutcast2_multi index e1202ad8..e862fffc 100755 --- a/plugins/other/shoutcast2_multi +++ b/plugins/other/shoutcast2_multi @@ -32,6 +32,7 @@ use strict; use warnings; use LWP::UserAgent; use XML::Simple; +use Data::Dumper; use Munin::Plugin; need_multigraph(); @@ -146,12 +147,22 @@ sub main { } my $streamConfigRef = $adminRef->{STREAMCONFIGS}->{STREAMCONFIG}; my $sidDataRef; - foreach my $sid (keys %{$streamConfigRef}) { + if ($adminRef->{STREAMCONFIGS}->{TOTALCONFIGS} == 1) { + my $sid = $streamConfigRef->{id}; my ($return,$tmpSidRef) = fetch_sid_data($sid); if ($return == 0) { - next; + # Only one stream, and we didn't get a good response. + exit; } $sidDataRef->{$sid} = $tmpSidRef; + } else { + foreach my $sid (keys %{$streamConfigRef}) { + my ($return,$tmpSidRef) = fetch_sid_data($sid); + if ($return == 0) { + next; + } + $sidDataRef->{$sid} = $tmpSidRef; + } } print_active_data($sidDataRef); print_listener_data($adminRef->{STREAMCONFIGS}->{SERVERMAXLISTENERS}, $sidDataRef); @@ -209,12 +220,22 @@ sub config { } my $streamConfigRef = $adminRef->{STREAMCONFIGS}->{STREAMCONFIG}; my $sidDataRef; - foreach my $sid (keys %{$streamConfigRef}) { + if ($adminRef->{STREAMCONFIGS}->{TOTALCONFIGS} == 1) { + my $sid = $streamConfigRef->{id}; my ($return,$tmpSidRef) = fetch_sid_data($sid); if ($return == 0) { - next; + # Only one stream, and we didn't get a good response. + exit; } $sidDataRef->{$sid} = $tmpSidRef; + } else { + foreach my $sid (keys %{$streamConfigRef}) { + my ($return,$tmpSidRef) = fetch_sid_data($sid); + if ($return == 0) { + next; + } + $sidDataRef->{$sid} = $tmpSidRef; + } } print_active_config($sidDataRef); print_listener_config($sidDataRef); @@ -244,7 +265,7 @@ sub print_active_config { print "graph_title ".$graphsRef->{active}->{config}->{title}."\n"; print "graph_args ".$graphsRef->{active}->{config}->{args}."\n"; print "graph_vlabel ".$graphsRef->{active}->{config}->{vlabel}."\n"; - print "graph_category".$graphsRef->{active}->{config}->{category}."\n"; + print "graph_category ".$graphsRef->{active}->{config}->{category}."\n"; print "graph_info ".$graphsRef->{active}->{config}->{info}."\n"; # Print the Data Value Info foreach my $dsrc (@{$graphsRef->{active}->{datasrc}}) { @@ -279,7 +300,7 @@ sub print_listener_config { print "graph_title ".$graphsRef->{listeners}->{config}->{title}."\n"; print "graph_args ".$graphsRef->{listeners}->{config}->{args}."\n"; print "graph_vlabel ".$graphsRef->{listeners}->{config}->{vlabel}."\n"; - print "graph_category".$graphsRef->{listeners}->{config}->{category}."\n"; + print "graph_category ".$graphsRef->{listeners}->{config}->{category}."\n"; print "graph_info ".$graphsRef->{listeners}->{config}->{info}."\n"; # Print the Data Value Info foreach my $dsrc (@{$graphsRef->{listeners}->{datasrc}}) { From ee4bc102b9b497674e2762e5d9ab651c6f099095 Mon Sep 17 00:00:00 2001 From: Matt West Date: Thu, 9 Feb 2012 15:05:58 -0800 Subject: [PATCH 040/123] Removing unneeded data dumper module --- plugins/other/shoutcast2_multi | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/other/shoutcast2_multi b/plugins/other/shoutcast2_multi index e862fffc..4ff57589 100755 --- a/plugins/other/shoutcast2_multi +++ b/plugins/other/shoutcast2_multi @@ -32,7 +32,6 @@ use strict; use warnings; use LWP::UserAgent; use XML::Simple; -use Data::Dumper; use Munin::Plugin; need_multigraph(); From 8ffcf3ff69f6f7564cecddbd2189b771f9efc0e6 Mon Sep 17 00:00:00 2001 From: Matt West Date: Thu, 9 Feb 2012 15:24:34 -0800 Subject: [PATCH 041/123] Added perldoc info, tidied up a bit --- plugins/other/shoutcast2_multi | 97 ++++++++++++++++++++++++++++++++-- 1 file changed, 94 insertions(+), 3 deletions(-) diff --git a/plugins/other/shoutcast2_multi b/plugins/other/shoutcast2_multi index 4ff57589..9eca6500 100755 --- a/plugins/other/shoutcast2_multi +++ b/plugins/other/shoutcast2_multi @@ -17,6 +17,10 @@ port = port we need to connect to in order to get to admin.cgi pass = password to use to authenticate as admin user +=head1 AUTHOR + + Matt West < https://github.com/mhwest13 > + =head1 License GPLv2 @@ -139,8 +143,20 @@ main(); exit; +=head1 Subroutines + + The following is a description of what each subroutine is for and does + +=head2 main + + This subroutine is our main routine should we not be calling up autoconf + or config. Ultimately this routine will print out the values for each graph + and graph data point we are tracking. + +=cut + sub main { - my ($returnBit,$adminRef) = fetch_admin_data($ua,$host,$port); + my ($returnBit,$adminRef) = fetch_admin_data(); if ($returnBit == 0) { exit; } @@ -168,6 +184,16 @@ sub main { return; } +=head2 print_active_data + + Thie subroutine prints out the active graph values for each stream and ultimately for + the entire shoutcast service. Should 1 Stream be active, but 5 streams available, + the global graph should show the state as active for the service, but clicking into + that graph, should give you a stream level view of which stream was in use during + what time periods. + +=cut + sub print_active_data { my ($sidDataRef) = (@_); my $globalActive = 0; @@ -187,6 +213,15 @@ sub print_active_data { return; } +=head2 print_listener_data + + This subroutine prints out the listener graph values for each stream and ultimately + adds all of the current users together to show that against the maxserver count in + the global graph. Clicking on the global graph will reveal a bit more information + about the users on a stream by stream basis. + +=cut + sub print_listener_data { my ($maxListeners,$sidDataRef) = (@_); my $globalListeners = 0; @@ -210,8 +245,18 @@ sub print_listener_data { return; } +=head2 config + + The config subroutine can be seen as the main config routine, which + will call up to your shoutcast server to figure out how many streams + you have running, and then print out the appropriate multigraph info. + Ultimately this subroutine will call two more routines to print out + the graph args / configuration information. + +=cut + sub config { - my ($returnBit,$adminRef) = fetch_admin_data($ua,$host,$port); + my ($returnBit,$adminRef) = fetch_admin_data(); if ($returnBit == 0) { # $adminRef returned a string, we'll just print it out. print "no (error response: $adminRef)\n"; @@ -241,6 +286,15 @@ sub config { return; } +=head2 print_active_config + + This subroutine prints out the graph information for our active graphs. + It prints the sub-multigraphs first based on stream id, and finally the + root active graph. Its not suggested that you mess with this routine + unless you fully understand what its doing and how munin graph_args work. + +=cut + sub print_active_config { my ($sidDataRef) = (@_); foreach my $sid (sort keys %{$sidDataRef}) { @@ -276,6 +330,15 @@ sub print_active_config { return; } +=head2 print_listener_config + + This subroutine prints out the graph information for our listeners graphs. + It prints the sub-multigraphs first based on stream id, and finally the + root listeners graph. Its not suggested that you mess with this routine + unless you fully understand what its doing and how munin graph_args work. + +=cut + sub print_listener_config { my ($sidDataRef) = (@_); foreach my $sid (sort keys %{$sidDataRef}) { @@ -311,8 +374,17 @@ sub print_listener_config { return; } +=head2 check_autoconf + + This subroutine is called up when we intercept autoconf specified in ARGV[0] + If we are able to connect to the shoutcast service as admin and fetch the main + admin stats page, we will return ok, otherwise we will return no and the error + response we got from LWP::UserAgent. + +=cut + sub check_autoconf { - my ($returnBit,$adminRef) = fetch_admin_data($ua,$host,$port); + my ($returnBit,$adminRef) = fetch_admin_data(); if ($returnBit == 0) { # $adminRef returned a string, we'll just print it out. print "no (error response: $adminRef)\n"; @@ -322,6 +394,15 @@ sub check_autoconf { return; } +=head2 fetch_sid_data + + This subroutine is called up to fetch information on a per stream mentality. + If we are able to connect to the shoutcast service and get the stats we will + return 1 and a hashref of the de-coded xml information, otherwise we return 0 + so that we know that we have failed and can handle it gracefully. + +=cut + sub fetch_sid_data { my ($sid) = (@_); my $url = 'http://'.$host.':'.$port.'/stats?sid='.$sid; @@ -334,6 +415,16 @@ sub fetch_sid_data { } } +=head2 fetch_admin_data + + This subroutine is called up to fetch information from the admin page to get stream ids. + This subroutine is also used to test that we can connect to the shoutcast service + and if not we can fail gracefully. If we are able to connect to the shoutcast service + and get the stats we will return 1 and a hashref of the de-coded xml information, + otherwise we return 0 so that we know that we have failed and can handle it gracefully. + +=cut + sub fetch_admin_data { my $url = 'http://'.$host.':'.$port.'/admin.cgi?sid=1&mode=viewxml&page=6'; my $response = $ua->get($url); From d1b9cbbb909591746893e7b90a1d89beda7d560f Mon Sep 17 00:00:00 2001 From: Manuel Schneider Date: Fri, 20 Jan 2012 02:06:58 +0800 Subject: [PATCH 042/123] pretty simple plugin to monitor pending freebsd-updates requires 'portversion' --- plugins/other/freebsd-upgrades | 38 ++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100755 plugins/other/freebsd-upgrades diff --git a/plugins/other/freebsd-upgrades b/plugins/other/freebsd-upgrades new file mode 100755 index 00000000..e9b0b5a5 --- /dev/null +++ b/plugins/other/freebsd-upgrades @@ -0,0 +1,38 @@ +#!/usr/local/bin/bash + +if [ "$1" = "config" ]; then + echo "graph_title Available Updates" + echo "graph_args --base 1000 -l 0" + echo "graph_vlabel upgradeable packages/ports " + echo "pkg.label binary packages" + echo "ports.label ports" + exit 0 +fi + +if [ "$1" = "autoconf" ]; then + echo "yes" + exit 0 +fi + +updates="$(freebsd-update fetch | \ +grep -v 'Looking up update.FreeBSD.org mirrors' | \ +grep -v 'Fetching metadata signature' | \ +grep -v 'Fetching metadata index' | \ +grep -v 'Inspecting system' | \ +grep -v 'Preparing to download files' | \ +grep -v -e '^$' | \ +grep -v 'The following files will be added' | \ +grep -v 'No updates needed' | \ +grep -v 'The following files will be updated' | wc -l | sed -e 's/ //g' )" + +updates="$(echo -n "${updates}")" + +echo "pkg.value $updates" + + +updates="$(portversion | grep '<' | wc -l | sed -e 's/ //g')" +updates="$(echo -n "${updates}")" + +echo "ports.value $updates" + +exit 0 From f7fcd35f64e1b8b9cc5042f518c1010062a2b375 Mon Sep 17 00:00:00 2001 From: Sean Whitney Date: Sat, 28 Jan 2012 00:53:51 +0800 Subject: [PATCH 043/123] Converted script to perl. It now handles: * 32 to 64 bit counters * reboots without resetting graph values * a rolling 30 days with preliminary prediction * autodiscover a public IPv4 address on a linux firewall/router --- plugins/other/{network-usage => bandwidth_} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename plugins/other/{network-usage => bandwidth_} (100%) diff --git a/plugins/other/network-usage b/plugins/other/bandwidth_ similarity index 100% rename from plugins/other/network-usage rename to plugins/other/bandwidth_ From 138de37d29b929465390799ba22b2481c9f19fb4 Mon Sep 17 00:00:00 2001 From: Sean Whitney Date: Sat, 28 Jan 2012 00:57:58 +0800 Subject: [PATCH 044/123] perl version not committed?!? --- plugins/other/bandwidth_ | 296 ++++++++++++++++++++++++++++----------- 1 file changed, 218 insertions(+), 78 deletions(-) diff --git a/plugins/other/bandwidth_ b/plugins/other/bandwidth_ index 6d73fdbf..99a4fc39 100755 --- a/plugins/other/bandwidth_ +++ b/plugins/other/bandwidth_ @@ -1,12 +1,10 @@ -#!/bin/bash +#!/usr/bin/perl +# -*- perl -*- -# Now updated to support 32-bit operating systems -# Requires bash - -: << =cut +=head1 NAME bandwidth_ - Wildcard-plugin to monitor total network traffic and -predict 30 day bandwidth usage + predict 30 day bandwidth usage =head1 CONFIGURATION @@ -20,16 +18,21 @@ bandwidth_ to this file. E.g. Most likely usage is to monitor an interface connected to your ISP. +The suggest option will try and determine if you have any interfaces with a +public IP and if so it will suggest monitoring those interfaces. If all +IP addresses are private the setup will have to be done manually. Suggest +does not handle IPv6 addresses. + =head1 USAGE Any device found in /proc/net/dev can be monitored. Examples include ipsec*, eth*, irda* and lo. -Please not that aliases cannot be monitored with this plugin. +Please note that aliases cannot be monitored with this plugin. =head1 VERSION -$Id: bandwidth_,v 1.2 2011/09/16 17:54:50 root Exp $ +$Id: bandwidth_,v 1.35 2012/01/23 20:04:33 root Exp $ =head1 AUTHOR @@ -53,92 +56,229 @@ to be interested in expressing peaks in bytes.... =cut -# Change to show your outside interface -INTERFACE=${0##*bandwidth_} -HISTORY="/var/lib/munin/plugin-state/bandwidth_$INTERFACE.state" +use strict; +use Storable qw(store retrieve); +use Switch; -TOTALUPTIME=0 -TOTALINPUT=0 -TOTALOUTPUT=0 -OLDUPTIME=0 -OLDINPUT=0 -OLDOUTPUT=0 +my $interface; +my $history; +my $counter_input; +my $counter_output; +my $input; +my $output; +my $uptime; +my $oldest_ts; +my $input_30days; +my $output_30days; +my $perf_ref = {}; +my $count30 = 2592000; # The number of seconds in 30 days +my $unix_ts = time; +my $rollover = 4294967295; -test -f $HISTORY || touch $HISTORY -BC=$(which bc) -test ${#BC} || (echo "bc not found, please install bc" && exit 1) +eval { init(); }; -case $1 in +sub autoconf { + $0 =~ /bandwidth_(.+)*$/; + $interface = $1; + exit 2 unless defined $interface; + $history = "/var/lib/munin/plugin-state/bandwidth_$interface.state"; +} -# I know that "bandwidth is bits and base10 as opposed to bytes and base2. -# However the purpose of this plugin it to monitor your monthly bandwidth -# consumption to make sure you don't go over your ISP's peak. ISP's seem -# to be interested in expressing peaks in bytes.... +sub bit32or64 { + if ( $input > $rollover || $output > $rollover ) { + $rollover = 18446744073709551615; + } +} - config) - cat <<'EOM' +sub retrieve_history { + return (undef) unless ( -r $history ); + my $store = retrieve($history); + while ( my ( $key, $value ) = each(%$store) ) { + if ( $unix_ts - $key < $count30 ) { + $perf_ref->{$key} = $value; + } + if ( $key =~ /last/ ) { + $perf_ref->{$key} = $value; + } + + } +} + +sub suggest { + + # This only works if one of your interfaces has a public IP, + # Otherwise it will fail and you will have to set it manually + # Multiple public IP addresses can be detected. It won't + # Detect IPv6 addresses. + my $locate = readpipe("locate -b '\\ifconfig'"); + my @ifconfig = readpipe($locate); + my @old; + my $net = "/proc/net/dev"; + my @interfaces; + -f $net || die "Unable to read $net: $!"; + open( DEV, "<", $net ) || die "Unable to read $net: $!"; + + while () { + chomp; + split; + /Inter|face/ and next; + split /:/; + push( @interfaces, $_[0] ); + } + close(DEV); + + foreach (@ifconfig) { + if (/inet addr:([\d.]+)/) { + $1 + =~ /^(127\.\d+|10\.\d+|172\.(1[6-9]|2\d|3[0-1])|192\.168)(\.\d+){2}$/ + and next; + exists $interfaces[ $old[0] ] and print "$old[0]\n"; + } + @old = split; + chomp @old; + } + exit 0; +} + +sub store_history { + + # Store the current values to the new old times + $perf_ref->{$unix_ts} = { + input => $input, + output => $output, + }; + $perf_ref->{last} = { + counter_input => $counter_input, + counter_output => $counter_output, + uptime => $uptime, + }; + store( $perf_ref, $history ) || die "Unable to store $history: $!"; +} + +sub arg { + defined( $ARGV[0] ) or return; + switch ( $ARGV[0] ) { + case 'autoconf' { print "yes\n"; exit 0; } + case 'config' { print_config(); } + case 'suggest' { suggest(); } + } +} + +sub print_config { + print <; + chomp $counter_input; + close($rx); + open(my $tx , "<", "/sys/class/net/$interface/statistics/tx_bytes" ) + || die "Unable to read: $!"; + $counter_output = <$tx>; + chomp $counter_output; + close(DEV); +} -INPUT=$(ifconfig $INTERFACE|grep bytes|awk '{print $2}'|sed s/bytes://g) -OUTPUT=$(ifconfig $INTERFACE|grep bytes|awk '{print $6}'|sed s/bytes://g) +sub uptime { + my $puptime = "/proc/uptime"; + open( TIME, "<", $puptime ) || die "Unable to read $puptime: $!"; + while (

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 N?~d&4RB^2r{|{hmL5TnW literal 0 HcmV?d00001 diff --git a/images/network/tcp_retries.png b/images/network/tcp_retries.png new file mode 100644 index 0000000000000000000000000000000000000000..19582809113c9ba5d5508c30b961aafcae76f41d GIT binary patch literal 20610 zcma+41z1#T8#W9NqJlvi)PRJ5qI9PqrL>?Z64D(*H;9C^SfDg22uO*9q>|Fz-QC^a zwcO8+=Y603{{OKLx6I(IS?j*9Jg@V-1C$h`@y?Q;MWIl5cVr~)p-@<>@P`!lG`u6{ z+{_Dqoz|6=mOvdN|HYRize1s|pzcVBsX9e3#<*&z+8-Tj8^68vLF2;7<=}jY>??zK ztKx}YO@|i~7d;$htI{eISG&RE!F#O|9)47$7<+2>Oues@=uf#6rzz& zoAvORT(#jt-uf z&=02MZ(AKgcLb35gwaU*--qSJg&E>kwe&kU zsOhGd3hLt>X}h_OGYJU^q#XLVc^3WEu5SBVi|if;P8#2BBpoP1o)21fOOl-wm9kLt z5z;7jBI4)g|D2H_B`0?tA1&Y$iWWROmqNm57cgk)cz_vxeB)9uIrrIsfB-(s*hzC9 ziXoTzo94Y=ee;7!7!^iJ_m?Vy$vD69U4$oV)hM((gTY{)hlEHlR{eQ5AAEWERM>S3_pJ%XyDY;- z@{PI9bLr~2Ct6ly%tp(7XZmuP%gf8zFk=!LWgdeGMq0h$B-;GDySq+%v&pR)nu6a< z+D|Kr?9#JnmoC^f6xz=dqR}BQUJ#zAREJRbcXX&~6j)=y?*@w;Ytk}H=j<9B zhu!_*)rZfYal?eSBHLA-UDk5KTAymXedo>@Fg)v>6`f?g?ZtloFJEpT+pj2k$QA3h zc4uMWJIT9u@0hM%FDFqHdOF*lydIlV^i{aCKFeuU7ui`x#n{RK5=JFeRiCS}Z+|&> z484eo3cBr&Ps7Se5W%YL(8KAvWqw1ul+tl&0AmdAgu?or^{~FX36DOSq4|9sp8Qu` z-Pj>XdDtVAZ(9bjOO-wczkB>C9GvmB$@+q6s!?s3uVLS9CeKba$Ay`7XN(R6}y;Rmkztb{Wbm z;kLi-Tt^CGiRgWBAwMrK)X>+Dd->T%gr;gsp)~KQHa9nWzS}aseDx~)&6`OM*%)|w zzpM205*_s1Q{npHei_+hM& z#;74iTvu1u@=5k`%|44}Vf>dba(U}|6)_`B5)eCLjW(?%ZuLv8Uti9v&inc^1q;cp3#HRK8F8RUST^v0Ic5A@_A%F1ZYwTZycNNgI7@ zUSLA@(G@p}_c01w&RVXE+y{$Ro)yy6?mmHmX9tR%NuF65``;*)T@GeCdsBZh`&$CDbqS zr{9?$o0ou_UYKfl_pCmG6|vVyP9yy4P&C}^5QpQE21M9XY8e_onKO&MG2_+zTM(Rd zwibGwwg(+o>zFe~hFq5|?A*=C=LxbG1C8IeEZ8O1>hVN{zk3&e_^j1%>GR;=N?Fzt z`q^jq&gU35mVBbL#YLA)DU74G3|Cwb+0uV$ANl2bWMv{Ir>uzGl z%F5<(k_DqVeV){7X2#gZ&rkR0U{_&3iq{H1r#D#iV3Z&vBS=>7kS0%)NS-7mr`<#s z@}Nk~vd6YyFj}zdnm8k_@D(0M0nY4@OSfon%Z&G~iXK9bX_336hE-qCiMdV5+#b$iqh9@Q`b(V)pP+l!9N)po2=a6)R@D$x$VW&o1j2g3i9&A`GaUi9)j+)eB!*^4<*NLAs(=e$O* zo@?q`St&u!z)-HwF*;HE%G;&FVNB3*sS@(Ol7>cbgtkYoNq`JbY!|hM?xK3J-Q0zY zgXZIrPb$ePZ7C{F@Lt$v#Ov2GU$?t@b6#%TtM87|3K95~yf*LNm~5(ny_Qn!a?4ZX z&8xb)yOE8C-;7&Nz!u%gw;WV@^yu1WUUNopkYjX=fW0^vYYXfmq{p>6S~@x+$cEW^ zHHleSN?FBobEbDTBi_Ef+t%%IT#`6BFP|xeo>1A{ES*x}&cNDL)e?>ujjOu@tDuwp z(0ab>CSnZJ&2iH3&Z$km#mc9CHIo*7-+n@hfARM1)YoCl{0hSyB=3~CY>MB#8yGa@ z;LN{3Muxk#j_t97wLR=v_PP7$a9_49-+j+S5+3a6Xn$Ecu1{TZ6DvkwwvOu4rl|(6 zwpp_2H#6aH?H=3P?G{B_Qz9*o1udM{8Q@YoyBo7o+h*+S>=3@nOWpUgU|)I`9X95> z6_u=ZHY?3cO|$fB@QYWgDJ*O7OifK&O5I&wM?|bI#X(%?3Umr?dXoL{2~U*deCG7T zh{u^Su?Y3jp$*dxF_Aag%Vr|?$H`}YCL2cTJeAB|>+HbZTS*C$@6MEML{zyl=v#J&%Pf^ zm1hiIq>)?}6pWdz#M|d9QH&FL9u_9;kB?s2>bKH6+}lKraGi^J_hu;lF)yWv8|nPO z!D)>-VqbA>QlA13Ub^Ue|F%Z~9lf)DA(fMUv>5Lz@2_77U=Gs=-)V{wWOv)K<}&Ld z)EoY8i&NydT>UGYF2>F;ta5U!EB!IkjT=6VhS(?w2+El__>?a`iXL{V1-{MXE$KA% ztoxhsb!ukPc(Z!d>e|I zzqP2k?~v~AP#4>il<%8 zK5pk2&iibXGp`me1lo#LRfs*4t=AV*n?)6j;@L6({3JVOd=~3)S5=mYxa^!H^c-CG znTXY>_-R+I$__7C^yf)N@mL^LE)=NeOs~N|GB8(>vdXZTrs z*NW))npu1%y{KXr?zt;*b(!1Taqwss3nsx-E&hs{=J!@G0@{~&D~!zh3+opPTud=3kwx_WxqIu)mpVD?s8@Aq1QSvebDeT$ah~`noF(Gw7BcliHf$4*$nc&!oq6X_lOIl{)Pq;!b{dgoT{f zjb2MdV!)m@hs>^-w&8yhIOznOL-J*bSFoKLO3%^9U$QXb;r0u0?HR;}PP zwai2aaSy-hpE-?3e)3iA#LSDJz(6*g@{^RJ9s}%xCGPuM#goeP3WPqbIs(>~9rAyK zjb+$NFm34^Lp=7a7(vG@*KJFr@xEiUBXiJe6j$}#cG?;q$ZdZypOnj(5H zll6CHYKK9yh`M$6?pa9At5uh^xw;ev#q|Em+4RP$+p+i7QwG@3c{j-V6F+=_PQ@20 zdflqPiw}#P*pn1u0-+nKGtbLY`n{M5G8%#tft8MneZD(8P7A|j)L{Q8FQg^BcCGdf zqX0203yV~d!{W+jxAu4dW9%&rjc7f#vTHx&%yW9pQI(4NeAXjg^oO&VrS}l)On<<9 z<5E!K-aJ2l;a6#s65j5oeJNXu>WvQ94Rxc2`f_=0>!ct=dB0E>QGBkr=_jD`#@QjCw`JwB=s3&aLsEZ-dMhd)YvBgw3sz$~Q zN?lSxhZ|rxK2S{SPS(AFqCRx&bSgyLe;DPpkUulP%QtVT_8Ct$D*O>;NzsAG8iyCu z>J#TS&iwDRp-#)M_gG8}>%@r@ud^gcAWCt#q`@xN{*~?^xa}f|PKBQ_?~ddOrcmh{ z8X7wQ6rQvuN(NFSGrZ20|3_YfjsCjDlz_9*t$>9#GS|yI+w~ffgZGtS{Ez@Mf6H!? zZC`v!h{E6hkzD>v5eoX+iU<44Wi=fsDi|oNRqx*){XQLAVdb$-A+*_{QWyPH>hBG< zD~jEDeN81xx9V(1cQ-cb(oJRrrv& zQw2>vHhe*lI@}Mp!wVc{9E4Y&KgT_o*FF|EF}ZeER(3Va^8lOIa^S_!j_TFb8tM;V zl*YYZuM(4x5RsDpN(%}OmVgcoI>n&!a`B&QOvDjte^ zUSav%*|Th>9iOEl8CgGl`s6hA&M3=f@@|AXL;;>CJZER;na;G!_wL=(;L5MB*J=KB z>xm{h!^jBR>$6%;?~Oa2$0I+H>Pi^pqh0*@GnM1g%zst9x&0>h99|?&nx%K^38ojh zT-*vifAFPhF199&I#U!@p=8i<`YXJoo-d|iM?9$Pt$S_U0Pgi(Vt^Xhcm!i5B>;TWc`t3vkRh*PLw3u z7m*sa970A0x)4n=?fQ*n3RBejFO`$YfaEw8-D-UN6(|oz0Z2H7IyhKwHioAHF#00f zeZfDjW=)4-IFT%);cUmv(8R#Pf)AiZMXp)5^G{(C9nh2Z% z_ZQd&NHg@9tw`)Ifa1BT?*C{;x!tc%VG4)b<{G)7Njo(W)3deSEIJN-2EfC64y5t94@z_l32Y_Bhp>>}8NFvM-8y6P_ z05tLF=qN4$K@C7z0p8vx9tC%j{7d~-J6hq7^tO;|=$M#r0gmKz+4vkkq(>hc8TlNr zrwd?HIH=(Fb|2s~jlbc{22n^Yi zr4z}ncN%p?SQrhNmskU@;q%|YeBvO+$;RfZ%G*3C1M~V{zkalSkZvt;b+lU^Li-a? zSHly~2SHq}uAJ=r%XME@|0hJ=H1dxLr8{eIdlZN6Q!aGgc&wIPz zKiTHJ91m|?`v12NAVrgtbmQ|2uJ-3!jVH%>h(l7Ra-JZf6tKgEO470IITgt=RPv#e z{NB`nL)6yRX7!psfByVM<3p?8-v@0)k2#NAtPt6?Yu8xbJFidCh=@>CRaHTUUIX>v zsrnE9yRTDV(JFo(9ZjjKrbfrWa2lF<@tRQdDP-GWGVr82m`vu*ou3G93lS6PbjK7b##^?F2QEDMtpu7KB)$O#sPy209}GOMh>FJLpGR6cHh~LJIY2iL!;6+2 zBgF`fSq%y4F}<2l-$ugM_uUDc0P7TT4Sw{`6;tS)ewht z2SNEdd?*0ZH(kt=?bR~NW5b`uDaJ|ZjXqweM0_xLvoe2v$Okvr{WET zsP4he>cgxjSRTjw!$k{F&FAQGlm6~b=YIQ|qWQW{jm*K}_OP_PJf6pP(eP-3KUFz} zRdGUE^eh-=O#x;c@TBVgr`UKw`5%*$Kjh_Ig64!&!0z+EwQ!~ZVAPN%YapDgKp{P{ zwb&nuE6l>8zo5nLPI26@uKY0?bA~Be78)u<62WN6}a%U>a>ZG7`lF+r5 ze9YqfpDix^AC0AHz{IySXmhGyLbL)SEhe3?xj!0=Hd}@p!ZRh`@jDc9t-7I#Jlely zwW!(@Y;kXOhtC~)3O<_&+_v`iZ~(l)b;jUEDy%&BAK%BjPY#vcKTr_(@FUmPznd~I zhp_f*i*uUBa=*(Wy|KQ8e)iR|F0ouF`Rstg> zN>BMybv;SJL#6;`*v$XARq96OoE36p{cikvQJJ@(to}P7cUdlzTTL|*|TS1JanZcN{xw+4`E?pMRFLNh06Y8 z0mk$Y|v5~0LNEzKcJHN*Vg5IPZIV&N}8ZbveM%t*UIW@0gim( z?E%}KLi@C@T>-Zmng@Hx6-9&oCD6$VK?^6mLHHm;13E^h6HxL>3Or=&`;!YGDJi3% zAf=@ho9cIV)H12~^`i}B=B{JMMBI%(C^dHK)G27zUyQpyd2$6t!C#^cI7rCI0#;V6 zVOVj=*VoruAn?Dk8||OSnUKR6HV%H}+nbM3sH<16Mhd&~ynOlc0^Hb3SvR=KJ3GP16_Y5!4|7(o)R#D%r9Rg)YZvh{PfIx zs-GQ)9Ht`WGcA!w10$!AIUkT?6m5Il+-W!8g@myP?LwWy2wSl=QykU2JjQ|G3;w5?cJ`n=l?UkFbz-( z=UeYzP1;c5OsB(eHf+ehyYgHLd2-V0#8!99E_TaS^DOyXC^1M)1e6zXbNKpwhrb$T z6Wu6$1dRSqm&rqX3=<0?m;yg+TtK>W=tDqw%3BC#_6XJhqub+$?xAiB|V`{8$+ zt4R2TZu`Q;i!}WFWY90BlBaV0|8Ns>xY>Ui)nBOvn$nRUPkX)^oI}MU7HZTib?@HG zF~1aFEsi`4~AXwus|G%+w2m+$mdWpckhaWO^V;X-I;Um z)VHRdYWL`jh#Pl;HD7DWxdLjx^B#e){z5k3I%Hb{)8b*}7CaP4CWT^zhKJ`8FgI@L z>(ghxt*+c{P18InRv`XI2EsTP&00qFJ>N~=D|TKdq^1solmyVM9>6@7^{-BZ1es|6 z2c`S3Ws8zyNXE|qMgaEd^S4Y)QUor!c$ra0!IXStWwZhpX48)zks$+8z>KwSN{$`O zbh3C~(LnGoyUPLaY3a(wgJFFR1h<7q8Yu99=OqlG5k?9M!Q6q!@cqw_F<(_x5F3Ms zO4Pb}DTw}XGnx{a8&ILXMDbipEn7R<|82J9-HqLSDddaliw^H!qbIgX{sg~F20PQT zjPysQ+mjP`UeaMoMw?1u{(|&D7Xo-sl=Mjw`YM4Q>!e%|TsURE!qEiW2Q`p{~%j?&# zuW)mBF5a-pePWoLa~!z{TmrivvBIuOT3Vr2rMpsD*wa*ur$4w+bl?38AZbsc4B)+w zpy#FmH44RGfB-TYcYPsSDq0pPKH8gqm}3}1o|kgMz}z-dZ{cs}Y79BnfhLJc#DBvD zLAK`v^eEPy_I~0}SHiR`0ETJ@%g0BoP*;CWO}KlTtbd?ivbXA6cbc+4>u$$+PKQSh z+`SOS!rBMlG;i~1Ds~5pSuI{@S;7>~1Y5vk*KEwTW7enS4~CBqsjC4wDSmSu4DrBv zu`MmehxuN{8&<9EB&Q~%3xy8f2pP;+37e%2;27w2eR=anJY03*;%J_baUj=~;f+`6p`4Er` znc4#-0OoAS@D~9^fc2!refy2yyB@=wUexorU)Cc(u~CMi3r|l+mAh^4ZB(LpPC*Y@a7Hm$tFDp< zDRE=n(Uo(I`|=Jhtac@li!Sv(pLk0;=HKeew;5FD(VG!lIls}{moMgPP;FX-OE8(CU9Mdf@03+^-l`_6VNtkSU(bTBsXkP3!LRtKXIb?6 zao2J47jLoruAz&zv9R^c&E`lA7h z3=P;P20TJ@*~IM)(wMv2%Bjh!-GN!j-=LKE@0sYf1~u>3@t_Ud!&~76Uu)(+;aj?+ zB*)a@l@#esy{MK-dTx%q)lNmS{*LyXOzh>8G?6h{SR>sy9*ZB>xw7T0-eqkhMYs$W zsu#_1=tU(v#BU?*!fS@SS)euucT0yCl$!sSwW_U}QM-sfFn^WnMl>BG>nU zLs5F~+e$KV{sw^lN1q2&g&gzbv*Z-g;n0yY=Va)r_6b`so zq(JLJc#VKnpPK_#!e2ZD-n-R22?30Ejm%}g((?y_uE-Zv=7u zqMmye*b(03!?F}(YOBR?!A$=5r?t0l8H8k8^?n9D5locHh%zk>CnG5g3_O(S86b^jI#Tsh+qkFC~ z5G>++%kfCQNhW$Z{B@C4sGd#D4|UzD$mi_8umvcpd@8b`=ge;WlbAH)gB`tu^prR^-Z;IHk6rSHl5t0zLA9!q%!*P@>( zzxjYdwdS^3E{+=d-Q%!*Ma7Yq;#}%AJzw3}a+O=J->Ph`oyisaOTZwQBq~5@QSNcW zy=Vw3>(eliUq84@)^F!k1l@TK>7CAfi!IxOG7pWCF9xztAM^CH@F+fFzIjK_IQaOraS>cUKOw+^ z{B{|Mqhj29k(V1o9XhIUr>9Pr7xo6i3b(qHHG1Ig@S$LBoH}x9-U|2HUE3!95|th* z%FbiK+KV5r^I4)Hvf%wX)kYGI7rpFDdf}p&z&o!$=G%5fQMELK|9vJY3xYXVOu+dU zJchb{j~w;*?rl0BIk(mMKiW|2sqCw?^N_1a_EQwj4UqjFLAic1(iwXRw*k*?X8mBD z63t|F$i~|cQP58l21^OP-n@b0D(z{+8+NPw3 zLr^;*Dbq(_wTS6r0B4NXyxJ9*c1g>nFh5#jE`>P2I4u@eO|F4>gY2wyeVd4#5|WJ2V#_Pu|BNaBwX(cOByx zXDb&HZ#l-i!-UQo4x0Od5f?vPOYHyjAWb%vNRuMO>p4npCC*FeSjay{L%9D4UVoHK zqG=XwHb}fO&YBV1dOteF`mx6s&o8e^hcc-mY*@d`S?l&)+vh`NeKQ+ek%?^|^5%Fp zlHc2`%+ih@E3*FW6*~59YRm%fqn0?s$Z&mm@h6s7_RP(9#3vM*h4E3=j^zHy0WuBF zlqq~;4{E-hgY^Luk$9O)GaKF$V5^y~8Pk_iv^Bv;{dE6UuohG$F!D0deXO?tIPj10 z>1*4agVP@C*27ej;W?xuSqqT|Qo-ynoh{c^vh#JU+Sphwa#YVHdDMcJx%O9JH)ywf_# zeo3$o?>X)*T4Hofe?^(S^Ovb6=?YzcegoEZJ2`N(EU8Bry3x~K+8LZ`&2mx;`xxzs z-D#RDv_CNCw<&u_E?3RGyW=AlIY{ezm%nv`SEq9q#-n)5`WJwU%X?iNmPw1r*b+aU=qE(?vj&hx+h_q-hIFvMm^Ndln{cjivqI}-Up+fZG zXLeJK*W<@sjSnbq@Xsh~i)IXARLY1pE?Si|_*c$6Zw)1oMm^Nd(Ne0f*?YfP9A?uy z$;pF-H=eD#5iBofRN>ouiIB+aGe-G2`Q^!;sU=D&>EP-RABxa5FsW7D+8_n>EjnGp zDX#qr{^DLMjg19X+=ca1Ucxn71<{=Qq*KxF)|=LwZ`>b!>dv1lr6Xdi=zy;th@!^xbsz(sKEO0 z)w>t9W(51}xk`_P)onX!wc-=T3ER#!QoKktVxYG1_w-r9wtTFsUG#xl`c%IJd~f^a z7nYO<76yC{CFay@uh3*sn>^L>dSfBaAjYhO1muVo-^k({{H5H*6y|~o#hv$D6D*kN zr8aE9p=+PrNSPg9)2Zr(ZNQ*ke&QSl2?r53%UQ%y zJUl9sG{praLW0WSma_&9Yoa+FgCh?;t798{atN-b+Ou=XY zFE;H7r%WvE-t+32=WAkQ6Z6?E$w{mzkpUY;z&U$9@$egQv5jDv8kP%t(eC#@{L_3#N%ba=g;i$U2&o*5 zVT}_$FE{N79yV9gDJUS$z8E?fTj26zvlhDuZ*8_$M0Z%wZOeLdfHh;0xbKYCGYDTb z5rkX0O0?%<-_Q5JNJ5Fesd`!v<50~rZp;?u) zeg_;+&ly%4@JKjAc89XTuFZ%+U$;7jOktA4VO8Gegje_blBsWT!a4zuMNkoI!n3?o zJu|AMFVGEU>iXn+9UEd$GO$c)*?f)5JUEs(s!5=JC0ISan)^w^QVCkOSyl@yC=x-H3VGt+b^zsU?-EHNzQ;LY&|R`bw%-UOk7bf}r5C7$$7> z0_M!@pn=z`R2&V){et#&`SUhLWvV5r$G>z)9EsmYuDw|ckDO0R64tH58L_i+E^PMM z(Cw2117+M78jNblz395^dOc3CmvE%{!Ze;6NlU>i(|6874MuqVQVDX;bQy-Oh4(6? z6n6M9V zF`e*LFmIQ5Eh_*4==a)e3v=L*e0Kl;UK)0l~-uUBc)Yni6mi&Kfh<${$o_ z7i{C;fiyYuhF*pBY(H&Txp>)dKvQn_Rz+k%q{Kblbu#PhhZ-thPL#uhNhcD zF=`?$1?(7y6SOowUpvmE^YuhJ%%zO0(FIYrv?%PpEi~w-QT6}Ks%qHm(R~iQxymws zU7>*3`w)+6ZjB1sO_6z`Yu!Xh*!_hKp022| z(_}_&lV4Pr*4^pRYtJb6r?cgD?->UVMQN<+hK@>&QC&X9*y6i&OU`k(AUJO?SwDnJ z^t-ejhR@mBhX)5Q-n|IW!Bh}(v)r{FBM_`mG8AXHfx#2~Ii?UvR7YCbL&LS|pQacj zXSQ?a;Q*VmOUk6|yR343-9otEw|qhUYh>==kPtCkzYUw_jsa$mcFCT6oc^H3J`G~w zR$(^hoyEK*hxb{q0J71NcDupC4V6re9tM4A+ri3jD)BmF@r8GxR^7c zt=eqb*6)5WY?$V%vzH5%S5YyjPziFP`>8|(ceRStiwgB$taA~CrcW#AkLQr=T-A#r zJ+|gFUz;shCM$^yc#Z-hz$pOfeuDHya{iOyhKac!jUtKAM&$iFvjQ7$a|7H`i?!zW zZ_TlLL>;b(?xLeT3TrgiI(?)tTJqD5^N29g3EMH8^0b3=QmZODVvPKnN76t%98NSzk+Gb7P{O*E}U>910#H}`9)F`<*ikAS;K_*}wf}#Z^WXo^-o_3M^lcDe_Q>@6x z!HGqv>#6|;l06;X4PJR>Z5|mzYPI?aPn?m+{GYMUYwbi9_VznAFm*|cCsjO#!}ixa zQl4ya?niE8QR5VR9GWzJX8~(U>9QnZ8Dk!|`DizXzC;NSPHGuS_nb&luNi zb)f~vTGg~AJ6_NbpTB@Z5-?2XT6-PH4o`s?gF*r7j#+#^zw=zM5ola1z~>^_<~r&QWjr%okQ<(egKCSAJuq|9D68>XqvWf*J%`x&osNy)%j z9ADqjqOTl-J^vWuFSaQh6FomDJ2!LObiM2Y1v*X`ZSd`Q`NSS!_hDjPgmQDgg7C~6 z%gc@4&)BV&j}x6AlB(wjChYC`iJoFNXQaAt!3P)xh|&r8fs#ru=4r8)xP)Ccek4i; zQ7%t6yra8D%*w=c3$$vI^JWB;x9WGC{x6cye-tYQ>ksU>mP~qy4;kgly+jYsY=1MK zz0a@9z7Xl8t4j;hZso_1gElto0D$gb#=HF_ekD-HO+rFKrfqm{mJtEu|0e(cErc;3 z;nv4xut0f=5`;_3*1C;Tf}euUA`#&&PRWMgUYu^A^KSt_UVI}Ug^poFt@-qGDY zaTTC4f4A$R6DoRQF-|1$+GQSmj!Tp%Q0|@o`SWLi-7GO`1n2?JL*A_IpThvJGg!`SGf~S9TgMC}auv3yA(sKQqeOS74Rl$8#dO z7q-0ZT_8^i0D&kiXO7OrBw)56(oCb~*s3g@3PNIHZ+|Km++ojSVHB>VQBS@Vb%P-% zqA8151gRdvuDqwBlI^&xg}P;7aT8%C18Q{5y)Q74G42F194fDgOVACDZfp93sy-I$ZYD16qoL{n@K+ zj|!}5Vx6bR&c7{S$sKBWrMUQijFCT2oN{h`r3gGrMDqo@2pR!_KB5wGJh-<}7@8nl z*S=iaIIHLvIPYVIBz`NMftnKy(0QEu78$}Q1&UcB*aNUuaPje_G&N&xFHcYVgXH+< zBEzdQAlLci^rfHb5Zc4{UDOs5v17L(a{kPm?;x0tJ*^G8wpmC zMIbR94Il4&0u_b{L`JLAqQ_*HSqd^J4Nq>2src1Uxt>R0yfTl&3&K{~Al=|)mkOl} z*dB5vpp)Xd5eKp|A8xa*Ck>H)l?Q+lIvpQwE32!YEgD4R(E0sXBRS0?6~HTAnT*gb z>|lH?lbo7bXR^@s8O;|@9mfF%qzn4c=K%rAGr@19Sw-A8TBWfLzFL0s)n9@6JRvo5 zEh)O%W)i>$&d5Q0}yGY({zj@ zedg2j%Nt|uFJBVNz2`cE(6HbSLe3rf+>Sc~Hv_y~Cb<}a&ec=iO89rT#1YZpXbz29gM!-L*;M?hLegzGVmm7c6V{iFyw!j(012^OwsO87F$+W`2vtFh$iv!XJh$~mUWC=W0wM~&JUme7gHRhAwJ=z8`OKL! zC}hx!oDLubY`tt?VMpZ2uH-8wS)9hfp%W1q8tq5;+iMfGC@)0a4vzP-on8GFtl+?> zZ{IG!Q1OME27DAVT7C+=b~rnPisZN)*rH(|Tps}zpkp7)#0S@G=|Bw=WEiA7^Z zd07^?b;ukQ;glf^KF}?qet?+3mkG#3G;;C^v}fw zUyZrBIYLUx1t(ZWWDW`}oX?h-L?bNTbW`qK)X-t60;q*R^gr!X=Dco5&&c>8 zG4TXK_yUn5JJ97p1xo{LM@S1WqCliuz<2|e->E0E@f>b-jq zKRR3eg68(hV*W5K56=VU_G^v;V%B85mXuRXF+pu@_dpY%14HxQs#=7BmYbXVu*{>R z9@Fu({#^T0l8l9+5(>}5d2OBi{?+cXqfT}fDoV;=pwkPjH*g{e$lKc+#pT9+2=Z!W zP!Od*&NnqFOmJGv?OtgTTpj^JA;Ktt*)kT2o0~h5$ATOQASUfe{0}_aiNOoTSU|me z3ruD;P5baHteON2Vo-=40?D{88#7N~1{-uesG4H1f{I|8g5t73b$4%xYrfv#3K5+Y zpS&f7sE3e3G~cMxYkU+0NW@D7?B{%dz>xSTM_XjAHmOPl`fNpJ&$e+pZ#b$0Chd-hm_-jxq(GRzT~mY{KZF#Lryqcq%agvU zJqe^}yVHA+@-+t>eaJ|`>Is#muYLJi`>z)Bwa4Tu?T;;po zkK&%Op+E{Q2l8Gpu%ngjXYsyMDbCKaM^GJp;Ssmksb?*#^uoeM=mbBX;phP+(z2eY zO&gAI0FevOgqZyBDEtx=X{(wfmt{76UzDcoseZmW1Uh&`A+DVktZuIOtt~dePqxS^| z`@_f8V1zn3y=-vQLol`IkOjZ^UbmJj9(XNMb&HF8k2}tkK3CPoosCLW^z9o1lwftx zaaafT5p=XR#z?9zJ6yB^1xQuu{WLa%x(k_gMtS{<4#kj)Be_go3Nh-leH>c%%2_7u zFphgKq+kWZe)T-_Ss5AYr2&5Wt5<30=}&?TA%6UESkZ9!+T@o@b&kxh!*&s-r=1-a zSe}MQz5r+ufvOb`s(RR9OlpK#kP^Rmzi%DC-3;O0_Hx+B$P*-`L-@8aD--;<=ikhb zFh6GY1?mLEMPa9v)YPVp^Y3lK5h*hA@{&+JgoE!dESRH~`X8%E{0Lbsk z5k>(J8OocRo3l%xEI~?L+I=YPlKM2Xj%tDF6rATUo{}6Z z4kQbdeP8lij8oc!bO;7e3K>3uuS0oF$GYOP7O$KD1x=P|=kouCYZjDY218P(KZJ>tLtup?JVtw_ z%q&#vgst-XmEj-}SgFycI8@edwaOpdUEVCfaUf?z1w7TAzC_f@x{g=lA=dztMMARo zvuDa`YTH4|UxaeSzrWhGd2nvB=we+>F^I7B_4P*(J~jw)lH{ZOfL6`FWvl&t`sWqZ z&=!XT8!!o|*-m(RdP0o<0CDs4=g%wGlt=UNSCntSx z$HvB9?RsoAiX2V@wz0Q4?+?fQAV*GQ^)D)*tAL|B@5J@fH!4S+Y53-kxUgM>;te{Q zU?g!u%39y+PgCN*SC5QTk+*?6Xx{g&b~fk@@9xJ>JO0hhOBq3|?BIOJ88C2dX*eQA z`QE*;Hy%JEd06Cd6GR@rt2uZCDt+?HFD%=G~QseIM8O&_k)y#Q> z)sNV-{N?Qy@|10%3J{BAyKI_PR#u{vlHE<;F2I=s3X%M_42WpfZRIBp@^h^F)+1?g z=c&g-=U9so7E4E0m&?Ia%|y{DFXZfk4|okbR>;8u*?Q77d#$k>3&)(pcX#Vnzr{Rm zde3c!XkZ&6*|7%;?OzEOy%aoN+3g*)Hwnbmj1YTvTBqZ)GMr{39!4d+0vjYSTOdNz z#`-w#JVIS6S}qNOtf9R6S||G_3%g;5@<2K#_(^t;{rUE}>O4+Ri8{rT4-J!EUr?f%FsjTh?` z+E(a*ms>KAWW~cKJ>hJbneI$#;Mcr(@xlxGr3e=al~9d&LYd8{O8B-Z-^YBLWaV~s z>sC+yE+O^3V>XqT+COU8GbtYdNqrUR_(41dLCALEe1tk2nPZcF`$rzI@5K2VK1~H) z7GPDJ47yoEfs~};T;jXigYbJ11-V_fWqAe?|hO&8S;F6=@^-?rt)6o4ysdVj9T|i(UBF!*tibgujz_0VL zYdhoA$InHNcbi$>QGfcO7OJFR<2YOR-6cU|Qhd^>PNyk?S*y<3If_mbU)1%@f-js3 z5Tayho^Y_g)OF^?OQWdt^t80waHtR+vo5d!4-OBJvIZ9R1L)uXsJlWpPea=Y6%n^K zof@cA4qoDrx zUCcQ<*M&Dt^Ew_8J%eBX40DFxn?W*Sd5Fydkr+`RL&(VXICM#NZzwzg4GQKY8o^4S zwWplNbNZ_Ci|SMN8A1Wp?{oHQ`2AM!JL|_ykh+mmecX0N{hh``czmJNeKD4O54T9N`%6D+6z*u});WzM`} z<5pS(?L)pAPF0FQRMhxT=$8Y{Aq3h#l<`CFb*M3=Kq&YmR~@ZRBj~%Z3$nc|2o81& zJ*@`C8lmi%gmoLW%nJi=Dz>7MH%b2^Go}Rzv2qiX-WpT<@Iap_85o^`FrVeNYZDPY zq2aS3_fE6W4#I0*yPk8Y4FYOFW(Evr1irlL{U zCnG=wO2^Ot$c9Wew6?D91S-D|ng%yG?}wD%=Htlf`Fo4>xZ+yK#n6PLq|f2_q7mdA z5(9%z6XR75NuUp6L9<81dF<6*;}6WYCpwuRw80m1#qJh#FL1(KD`el~#RdF`T~8?g zp`8oSEOHnf_-;$JeP>f=UeCSJtEgh16^J4teqh80>pAAIBXBs@@6jJE}x zLsE;6&&lM22Hh>^F(gfbR_D|2t6%WVq#*@LO80M z5}_c>`oSZCY`0(r`@{+B=_d5hSD!8CMCfXuc{(*BaMYL^bOm=YK`{J^omX5OwcmNN z6Ho*vFCx2RX_-;uOIum)vCuSlnoFXuA(D5!t`qKy1S~?_&hFa{9-@<@!mkBmejk>1 zdJ^^ry5DEF|5rEsSENLWXb(iH3M6-*PSei5OYj-*EA%bF0@lb+Yz)WLYKh62AKQMN RfI|gQcO(@gGQ^*F|9>e-09yb6 literal 0 HcmV?d00001 diff --git a/plugins/ejabberd/ejabberd_scanlog b/plugins/ejabberd/ejabberd_scanlog new file mode 100755 index 00000000..1daf8626 --- /dev/null +++ b/plugins/ejabberd/ejabberd_scanlog @@ -0,0 +1,148 @@ +#!/usr/bin/env ruby +require 'yaml' + +# ejabberd_scanlog revision 1 (Feb 2012) +# +# Scans ejabberd 2.1.x log for known error signatures and counts them +# +# Required privileges: read ejabberd log (user ejabberd or, in some cases, root) +# +# OS: Unix +# +# Configuration: +# env.log: ejabberd log file (defaults to /var...) +# +# Author: Artem Sheremet +# + +LOG_FILE = ENV['log'] || '/var/log/ejabberd/ejabberd.log' +CACHE_FILE = '/tmp/ejabberd_scanlog_cache' # cache file position + +DEFAULT_CACHE = { :start => 0 } + +debug_mode = ARGV.first == 'debug' + +begin + log_info = YAML.load IO.read(CACHE_FILE) +rescue + log_info = DEFAULT_CACHE +end + +if File.size(LOG_FILE) < log_info[:start] + # logrotate? + log_info = DEFAULT_CACHE +end + +new_data = '' +File.open(LOG_FILE, 'r') do |flog| + flog.seek(log_info[:start]) + new_data = flog.read +end + +LABELS = { + :wait_for => 'EJAB-1482 Crash when waiting for item', + :ejabberd_odbc_failure => 'EJAB-1483 ODBC sup failure (wrong PID?)', + :ejabberd_odbc_failure_echo => 'EJAB-1483 ODBC sup wrong PID failure echo', + :dns => 'DNS failure', + :database => 'Database unavailable/too slow', + :auth_error => 'The auth module returned an error', + :timeout => 'State machine terminated: timeout', + :mysql_shutdown => 'MySQL disconnected', + :mysql_refused => 'Connecting to MySQL: failed', + :hook_timeout => 'Timeout while running a hook', + :sql_transactions_exceeded => 'SQL transaction restarts exceeded', + :unexpected_info => 'Unexpected info', + :other_sql_cmd_timeout => 'Other sql_cmd timeout', + :UNKNOWN => 'Unknown error/warning' +} +def log_type(text) + if text.include? 'ejabberd_odbc_sup' + :ejabberd_odbc_failure + elsif text.include? "mod_pubsub_odbc,'-unsubscribe" + :ejabberd_odbc_failure_echo + elsif text.include? 'You should check your DNS configuration' + :dns + elsif text.include? 'Database was not available or too slow' + :database + elsif text.include? 'wait_for_' + :wait_for + elsif text.include?('State machine') and + text.include?('terminating') and + text.include?('Reason for') and + text.include?('timeout') + :timeout + elsif text.include?('The authentication module') and + text.include?('returned an error') + :auth_error + elsif text.include?('mysql') and text.include?('Received unknown signal, exiting') + :mysql_shutdown + elsif text.include?('mysql') and text.include?('Failed connecting to') + :mysql_refused + elsif text.include?('ejabberd_hooks') and text.include?('timeout') + :hook_timeout + elsif text.include?('SQL transaction restarts exceeded') + :sql_transactions_exceeded + elsif text.include?('nexpected info') + :unexpected_info + elsif text.include?('timeout') and text.include?('sql_cmd') + :other_sql_cmd_timeout + else + puts "Cannot parse text: #{text}" if debug_mode + :UNKNOWN + end +end + +new_data.split("\n=").each { |report| + next if report.empty? + report =~ /\A(\w+) REPORT==== (.*) ===\n(.*)\z/m + type, time, text = $1, $2, $3 + next unless type and time and text + + log_info[type] = (log_info[type] || 0) + 1 + if sub_type = log_type(text) + log_info[sub_type] = (log_info[sub_type] || 0) + 1 + end +} + +log_info[:start] += new_data.size +File.open(CACHE_FILE, 'w') { |f| f.write log_info.to_yaml } + +if ARGV.first == 'config' + puts < Date: Tue, 21 Feb 2012 21:54:50 -0800 Subject: [PATCH 087/123] removing tcp_retries The same data are graphed by the netstat_multi plugin in munin trunk, so no need to have a redundant plugin. --- images/network/tcp_retries.png | Bin 20610 -> 0 bytes plugins/network/tcp_retries | 45 --------------------------------- 2 files changed, 45 deletions(-) delete mode 100644 images/network/tcp_retries.png delete mode 100755 plugins/network/tcp_retries diff --git a/images/network/tcp_retries.png b/images/network/tcp_retries.png deleted file mode 100644 index 19582809113c9ba5d5508c30b961aafcae76f41d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20610 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 RfI|gQcO(@gGQ^*F|9>e-09yb6 diff --git a/plugins/network/tcp_retries b/plugins/network/tcp_retries deleted file mode 100755 index 085695a4..00000000 --- a/plugins/network/tcp_retries +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/sh - -# tcp_retries revision 2 (Feb 2012) -# -# TCP retransmission rate. Useful for network debugging. -# -# Required privileges: none -# -# OS: Linux with procfs -# -# Author: Artem Sheremet -# - -#%# family=auto -#%# capabilities=autoconf - -TCPSTAT=/proc/net/tcp - -case $1 in - autoconf) - [ -r $TCPSTAT -o -r ${TCPSTAT}6 ] && echo "yes" || echo "no" - ;; - config) - cat < Date: Thu, 23 Feb 2012 19:58:17 +0000 Subject: [PATCH 088/123] Adding a new ZFS statistic plugin --- plugins/zfs/zfs_stats_ | 254 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 254 insertions(+) create mode 100755 plugins/zfs/zfs_stats_ diff --git a/plugins/zfs/zfs_stats_ b/plugins/zfs/zfs_stats_ new file mode 100755 index 00000000..d218e46d --- /dev/null +++ b/plugins/zfs/zfs_stats_ @@ -0,0 +1,254 @@ +#!/bin/sh +# ZFS statistics for FreeBSD +# Author: David Bjornsson +# +# Description: +# This is a rewrite of the zfs-stats-for-freebsd +# scripts by patpro. Rather then pulling the +# information from the zfs-stats utility, it +# pulls it straight from sysctl. +# +# Tested on FreeBSD 9.0-RELEASE +# +# Usage: zfs_stats_FUNCTION +# +# Available functions: +# efficiency - ARC efficiency +# cachehitlist - Cache hit by cache list +# cachehitdtype - Cache hit by data type +# dmuprefetch - DMU prefetch +# utilization - ARC size breakdown +# +#%# family=auto + +FUNCTION=$(basename $0 | cut -d_ -f3) +PAGESIZE=`/sbin/sysctl -n vm.stats.vm.v_page_size` +MEMSIZE=`/sbin/sysctl -n vm.stats.vm.v_page_count` +MEMMAX=`echo 'scale=2;' $PAGESIZE*$MEMSIZE | /usr/bin/bc -q` +BC='/usr/bin/bc -q' +SYS='/sbin/sysctl -n' + +# +# Sysctl macros +# + +ARC_HITS=`$SYS kstat.zfs.misc.arcstats.hits` +ARC_MISSES=`$SYS kstat.zfs.misc.arcstats.misses` + +DEMAND_DATA_HITS=`$SYS kstat.zfs.misc.arcstats.demand_data_hits` +DEMAND_DATA_MISSES=`$SYS kstat.zfs.misc.arcstats.demand_data_misses` +DEMAND_METADATA_HITS=`$SYS kstat.zfs.misc.arcstats.demand_metadata_hits` +DEMAND_METADATA_MISSES=`$SYS kstat.zfs.misc.arcstats.demand_metadata_misses` + +MFU_GHOST_HITS=`$SYS kstat.zfs.misc.arcstats.mfu_ghost_hits` +MFU_HITS=`$SYS kstat.zfs.misc.arcstats.mfu_hits` +MRU_GHOST_HITS=`$SYS kstat.zfs.misc.arcstats.mru_ghost_hits` +MRU_HITS=`$SYS kstat.zfs.misc.arcstats.mru_hits` + +PREFETCH_DATA_HITS=`$SYS kstat.zfs.misc.arcstats.prefetch_data_hits` +PREFETCH_DATA_MISSES=`$SYS kstat.zfs.misc.arcstats.prefetch_data_misses` +PREFETCH_METADATA_HITS=`$SYS kstat.zfs.misc.arcstats.prefetch_metadata_hits` +PREFETCH_METADATA_MISSES=`$SYS kstat.zfs.misc.arcstats.prefetch_metadata_misses` + +DMU_HITS=`$SYS kstat.zfs.misc.zfetchstats.hits` +DMU_MISSES=`$SYS kstat.zfs.misc.zfetchstats.misses` + +SIZE=`$SYS kstat.zfs.misc.arcstats.size` +MRU_SIZE=`$SYS kstat.zfs.misc.arcstats.p` +MAX_SIZE=`$SYS kstat.zfs.misc.arcstats.c_max` +MIN_SIZE=`$SYS kstat.zfs.misc.arcstats.c_min` +TARGET_SIZE=`$SYS kstat.zfs.misc.arcstats.c` + + +# +# Calculation macros +# + +ANON_HITS=`echo "$ARC_HITS-($MFU_HITS+$MRU_HITS+$MFU_GHOST_HITS+$MRU_GHOST_HITS)" | $BC` +ARC_ACCESSES_TOTAL=`echo "$ARC_HITS+$ARC_MISSES" | $BC` +DEMAND_DATA_TOTAL=`echo "$DEMAND_DATA_HITS+$DEMAND_DATA_MISSES" | $BC` +PREFETCH_DATA_TOTAL=`echo "$PREFETCH_DATA_HITS+$PREFETCH_DATA_MISSES" | $BC` +REAL_HITS=`echo "$MFU_HITS+$MRU_HITS" | $BC` + +CACHE_HIT_RATIO_PERC=`echo "scale=2 ; (100*$ARC_HITS/$ARC_ACCESSES_TOTAL)" | $BC` +CACHE_MISS_RATIO_PERC=`echo "scale=2 ; (100*$ARC_MISSES/$ARC_ACCESSES_TOTAL)" | $BC` +ACTUAL_HIT_RATIO_PERC=`echo "scale=2 ; (100*$REAL_HITS/$ARC_ACCESSES_TOTAL)" | $BC` +DATA_DEMAND_EFFICIENCY_PERC=`echo "scale=2 ; (100*$DEMAND_DATA_HITS/$DEMAND_DATA_TOTAL)" | $BC` +DATA_PREFETCH_EFFICENCY_PERC=`echo "scale=2 ; (100*$PREFETCH_DATA_HITS/$PREFETCH_DATA_TOTAL)" | $BC` + +ANONYMOUSLY_USED_PERC=`echo "scale=2 ; (100*$ANON_HITS/$ARC_HITS)" | $BC` +MOST_RECENTLY_USED_PERC=`echo "scale=2 ; (100*$MRU_HITS/$ARC_HITS)" | $BC` +MOST_FREQUENTLY_USED_PERC=`echo "scale=2 ; (100*$MFU_HITS/$ARC_HITS)" | $BC` +MOST_RECENTLY_USED_GHOST_PERC=`echo "scale=2 ; (100*$MRU_GHOST_HITS/$ARC_HITS)" | $BC` +MOST_FREQUENTLY_USED_GHOST_PERC=`echo "scale=2 ; (100*$MFU_GHOST_HITS/$ARC_HITS)" | $BC` + +DEMAND_DATA_HIT_PERC=`echo "scale=2 ; (100*$DEMAND_DATA_HITS/$ARC_HITS)" | $BC` +DEMAND_DATA_MISS_PERC=`echo "scale=2 ; (100*$DEMAND_DATA_MISSES/$ARC_HITS)" | $BC` +PREFETCH_DATA_HIT_PERC=`echo "scale=2 ; (100*$PREFETCH_DATA_HITS/$ARC_HITS)" | $BC` +PREFETCH_DATA_MISS_PERC=`echo "scale=2 ; (100*$PREFETCH_DATA_MISSES/$ARC_HITS)" | $BC` +DEMAND_METADATA_HIT_PERC=`echo "scale=2 ; (100*$DEMAND_METADATA_HITS/$ARC_HITS)" | $BC` +DEMAND_METADATA_MISS_PERC=`echo "scale=2 ; (100*$DEMAND_METADATA_MISSES/$ARC_HITS)" | $BC` +PREFETCH_METADATA_HIT_PERC=`echo "scale=2 ; (100*$PREFETCH_METADATA_HITS/$ARC_HITS)" | $BC` +PREFETCH_METADATA_MISSES_PERC=`echo "scale=2 ; (100*$PREFETCH_METADATA_MISSES/$ARC_HITS)" | $BC` + +DMU_TOTAL=`echo "$DMU_HITS+$DMU_MISSES" | $BC` +DMU_HITS_PERC=`echo "scale=2 ; (100*$DMU_HITS/$DMU_TOTAL)" | $BC` +DMU_MISSES_PERC=`echo "scale=2 ; (100*$DMU_MISSES/$DMU_TOTAL)" | $BC` + +if [ $SIZE -gt $TARGET_SIZE ]; then + MFU_SIZE=`echo "$SIZE-$MRU_SIZE" | $BC` +else + MFU_SIZE=`echo "$TARGET_SIZE-$MRU_SIZE" | $BC` +fi + + +efficiency() { + + if [ "$1" = "config" ]; then + echo 'graph_title ZFS ARC Efficiency' + echo 'graph_args -u 100' + echo 'graph_vlabel %' + echo 'graph_category ZFS' + echo 'graph_info This graph shows the ARC Efficiency' + + echo 'hits.label Cache Hit Ratio' + echo 'misses.label Cache Miss Ratio' + echo 'actual_hits.label Actual Hit Ratio' + echo 'data_demand_efficiency.label Data Demand Efficiency' + echo 'data_prefetch_efficiency.label Data Prefetch Efficiency' + + exit 0 + else + echo 'hits.value ' $CACHE_HIT_RATIO_PERC + echo 'misses.value ' $CACHE_MISS_RATIO_PERC + echo 'actual_hits.value ' $ACTUAL_HIT_RATIO_PERC + echo 'data_demand_efficiency.value ' $DATA_DEMAND_EFFICIENCY_PERC + echo 'data_prefetch_efficiency.value ' $DATA_PREFETCH_EFFICENCY_PERC + fi +} + +cachehitlist() { + if [ "$1" = "config" ]; then + echo 'graph_title ZFS ARC Efficiency: Cache hits by cache list' + echo 'graph_args -u 100' + echo 'graph_vlabel %' + echo 'graph_category ZFS' + echo 'graph_info This graph shows the ARC Efficiency' + + echo 'cache_list_anon.label Anonymously Used' + echo 'cache_list_most_rec.label Most Recently Used' + echo 'cache_list_most_freq.label Most Frequently Used' + echo 'cache_list_most_rec_ghost.label Most Recently Used Ghost' + echo 'cache_list_most_freq_ghost.label Most Frequently Used Ghost' + + exit 0 + else + echo 'cache_list_anon.value ' $ANONYMOUSLY_USED_PERC + echo 'cache_list_most_rec.value ' $MOST_RECENTLY_USED_PERC + echo 'cache_list_most_freq.value ' $MOST_FREQUENTLY_USED_PERC + echo 'cache_list_most_rec_ghost.value ' $MOST_RECENTLY_USED_GHOST_PERC + echo 'cache_list_most_freq_ghost.value ' $MOST_FREQUENTLY_USED_GHOST_PERC + fi +} + +cachehitdtype() { + if [ "$1" = "config" ]; then + echo 'graph_title ZFS ARC Efficiency: Cache hits by data type' + echo 'graph_args -u 100' + echo 'graph_vlabel %' + echo 'graph_category ZFS' + echo 'graph_info This graph shows the ARC Efficiency' + + echo 'data_type_demand_hits.label Demand Data Hit Ratio' + echo 'data_type_demand_misses.label Demand Data Miss Ratio' + echo 'data_type_prefetch_hits.label Prefetch Data Hit Ratio' + echo 'data_type_prefetch_misses.label Prefetch Data Miss Ratio' + echo 'data_type_demand_metadata_hits.label Demand Metadata Hit Ratio' + echo 'data_type_demand_metadata_misses.label Demand Metadata Miss Ratio' + echo 'data_type_prefetch_metadata_hits.label Prefetch Metadata Hit Ratio' + echo 'data_type_prefetch_metadata_misses.label Prefetch Metadata Miss Ratio' + + exit 0 + else + echo 'data_type_demand_hits.value ' $DEMAND_DATA_HIT_PERC + echo 'data_type_demand_misses.value ' $DEMAND_DATA_MISS_PERC + echo 'data_type_prefetch_hits.value ' $PREFETCH_DATA_HIT_PERC + echo 'data_type_prefetch_misses.value ' $PREFETCH_DATA_MISS_PERC + echo 'data_type_demand_metadata_hits.value ' $DEMAND_METADATA_HIT_PERC + echo 'data_type_demand_metadata_misses.value ' $DEMAND_METADATA_MISS_PERC + echo 'data_type_prefetch_metadata_hits.value ' $PREFETCH_METADATA_HIT_PERC + echo 'data_type_prefetch_metadata_misses.value ' $PREFETCH_METADATA_MISSES_PERC + fi +} + +dmuprefetch() { + + if [ "$1" = "config" ]; then + echo 'graph_title ZFS DMU prefetch stats' + echo 'graph_args -u 100' + echo 'graph_vlabel %' + echo 'graph_category ZFS' + echo 'graph_info This graph shows the DMU prefetch stats' + + echo 'hits.label Hit percentage' + echo 'misses.label Miss percentage' + + exit 0 + else + echo 'hits.value ' $DMU_HITS_PERC + echo 'misses.value ' $DMU_MISSES_PERC + fi +} + +utilization() { + + if [ "$1" = "config" ]; then + echo 'graph_title ZFS ARC Size' + echo 'graph_args --base 1024 -l 0 --vertical-label Bytes --upper-limit '$MEMMAX + echo 'graph_vlabel Size in MB' + echo 'graph_category ZFS' + echo 'graph_info This graph shows the ARC Size utilization' + + echo 'max_size.label Maximum Size' + echo 'max_size.draw AREA' + echo 'target_size.label Target Size' + echo 'target_size.draw AREA' + echo 'size.label Size' + echo 'size.draw AREA' + echo 'recently_size.label Recently Used Cache Size' + echo 'recently_size.draw AREA' + echo 'frequently_size.label Frequently Used Cache Size' + echo 'frequently_size.draw AREA' + echo 'min_size.label Minimum Size' + echo 'min_size.draw AREA' + + exit 0 + else + echo 'max_size.value ' $MAX_SIZE + echo 'target_size.value ' $TARGET_SIZE + echo 'size.value ' $SIZE + echo 'recently_size.value ' $MRU_SIZE + echo 'frequently_size.value ' $MFU_SIZE + echo 'min_size.value ' $MIN_SIZE + fi +} + +[ "$1" = "config" ] && echo "graph_category zfs" + +case "$FUNCTION" in + efficiency) + efficiency $1 + ;; + cachehitlist) + cachehitlist $1 + ;; + cachehitdtype) + cachehitdtype $1 + ;; + dmuprefetch) + dmuprefetch $1 + ;; + utilization) + utilization $1 + ;; +esac From 281bd584c43a02c42958ef775270cbb3274caa1d Mon Sep 17 00:00:00 2001 From: Matt West Date: Thu, 23 Feb 2012 18:24:31 -0800 Subject: [PATCH 089/123] Fixing Evictions Stats Key Collision --- plugins/memcached/memcached_multi_ | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/memcached/memcached_multi_ b/plugins/memcached/memcached_multi_ index e574861f..3df9f8bb 100755 --- a/plugins/memcached/memcached_multi_ +++ b/plugins/memcached/memcached_multi_ @@ -810,6 +810,9 @@ sub fetch_stats { while (my $line = <$s>) { if ($line =~ /STAT\s(.+?)\s(.*)/) { my ($skey,$svalue) = ($1,$2); + if ($skey eq 'evictions') { + $skey = 'evictions_active'; + } $stats{$skey} = $svalue; } last if $line =~ /^END/; From 8315962063d51a9cbc7eb01f5232b79e7209baa8 Mon Sep 17 00:00:00 2001 From: dabb Date: Fri, 24 Feb 2012 11:51:03 +0000 Subject: [PATCH 090/123] Fixing typos in data type calculations --- plugins/zfs/zfs_stats_ | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/zfs/zfs_stats_ b/plugins/zfs/zfs_stats_ index d218e46d..bef05684 100755 --- a/plugins/zfs/zfs_stats_ +++ b/plugins/zfs/zfs_stats_ @@ -83,13 +83,13 @@ MOST_RECENTLY_USED_GHOST_PERC=`echo "scale=2 ; (100*$MRU_GHOST_HITS/$ARC_HITS)" MOST_FREQUENTLY_USED_GHOST_PERC=`echo "scale=2 ; (100*$MFU_GHOST_HITS/$ARC_HITS)" | $BC` DEMAND_DATA_HIT_PERC=`echo "scale=2 ; (100*$DEMAND_DATA_HITS/$ARC_HITS)" | $BC` -DEMAND_DATA_MISS_PERC=`echo "scale=2 ; (100*$DEMAND_DATA_MISSES/$ARC_HITS)" | $BC` +DEMAND_DATA_MISS_PERC=`echo "scale=2 ; (100*$DEMAND_DATA_MISSES/$ARC_MISSES)" | $BC` PREFETCH_DATA_HIT_PERC=`echo "scale=2 ; (100*$PREFETCH_DATA_HITS/$ARC_HITS)" | $BC` -PREFETCH_DATA_MISS_PERC=`echo "scale=2 ; (100*$PREFETCH_DATA_MISSES/$ARC_HITS)" | $BC` +PREFETCH_DATA_MISS_PERC=`echo "scale=2 ; (100*$PREFETCH_DATA_MISSES/$ARC_MISSES)" | $BC` DEMAND_METADATA_HIT_PERC=`echo "scale=2 ; (100*$DEMAND_METADATA_HITS/$ARC_HITS)" | $BC` -DEMAND_METADATA_MISS_PERC=`echo "scale=2 ; (100*$DEMAND_METADATA_MISSES/$ARC_HITS)" | $BC` +DEMAND_METADATA_MISS_PERC=`echo "scale=2 ; (100*$DEMAND_METADATA_MISSES/$ARC_MISSES)" | $BC` PREFETCH_METADATA_HIT_PERC=`echo "scale=2 ; (100*$PREFETCH_METADATA_HITS/$ARC_HITS)" | $BC` -PREFETCH_METADATA_MISSES_PERC=`echo "scale=2 ; (100*$PREFETCH_METADATA_MISSES/$ARC_HITS)" | $BC` +PREFETCH_METADATA_MISSES_PERC=`echo "scale=2 ; (100*$PREFETCH_METADATA_MISSES/$ARC_MISSES)" | $BC` DMU_TOTAL=`echo "$DMU_HITS+$DMU_MISSES" | $BC` DMU_HITS_PERC=`echo "scale=2 ; (100*$DMU_HITS/$DMU_TOTAL)" | $BC` From 6b7922c3793e6d4b8d92b7e1dafc75a9a41e9a7d Mon Sep 17 00:00:00 2001 From: dabb Date: Fri, 24 Feb 2012 12:46:10 +0000 Subject: [PATCH 091/123] Removing unnecessary graph_category attributes --- plugins/zfs/zfs_stats_ | 5 ----- 1 file changed, 5 deletions(-) diff --git a/plugins/zfs/zfs_stats_ b/plugins/zfs/zfs_stats_ index bef05684..d062c562 100755 --- a/plugins/zfs/zfs_stats_ +++ b/plugins/zfs/zfs_stats_ @@ -108,7 +108,6 @@ efficiency() { echo 'graph_title ZFS ARC Efficiency' echo 'graph_args -u 100' echo 'graph_vlabel %' - echo 'graph_category ZFS' echo 'graph_info This graph shows the ARC Efficiency' echo 'hits.label Cache Hit Ratio' @@ -132,7 +131,6 @@ cachehitlist() { echo 'graph_title ZFS ARC Efficiency: Cache hits by cache list' echo 'graph_args -u 100' echo 'graph_vlabel %' - echo 'graph_category ZFS' echo 'graph_info This graph shows the ARC Efficiency' echo 'cache_list_anon.label Anonymously Used' @@ -156,7 +154,6 @@ cachehitdtype() { echo 'graph_title ZFS ARC Efficiency: Cache hits by data type' echo 'graph_args -u 100' echo 'graph_vlabel %' - echo 'graph_category ZFS' echo 'graph_info This graph shows the ARC Efficiency' echo 'data_type_demand_hits.label Demand Data Hit Ratio' @@ -187,7 +184,6 @@ dmuprefetch() { echo 'graph_title ZFS DMU prefetch stats' echo 'graph_args -u 100' echo 'graph_vlabel %' - echo 'graph_category ZFS' echo 'graph_info This graph shows the DMU prefetch stats' echo 'hits.label Hit percentage' @@ -206,7 +202,6 @@ utilization() { echo 'graph_title ZFS ARC Size' echo 'graph_args --base 1024 -l 0 --vertical-label Bytes --upper-limit '$MEMMAX echo 'graph_vlabel Size in MB' - echo 'graph_category ZFS' echo 'graph_info This graph shows the ARC Size utilization' echo 'max_size.label Maximum Size' From 20cc0e049134bf8869a57163bd54672ea279b99f Mon Sep 17 00:00:00 2001 From: dabb Date: Fri, 24 Feb 2012 13:30:14 +0000 Subject: [PATCH 092/123] Added a L2ARC size function and some cleanup --- plugins/zfs/zfs_stats_ | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/plugins/zfs/zfs_stats_ b/plugins/zfs/zfs_stats_ index d062c562..81265ecd 100755 --- a/plugins/zfs/zfs_stats_ +++ b/plugins/zfs/zfs_stats_ @@ -59,6 +59,8 @@ MAX_SIZE=`$SYS kstat.zfs.misc.arcstats.c_max` MIN_SIZE=`$SYS kstat.zfs.misc.arcstats.c_min` TARGET_SIZE=`$SYS kstat.zfs.misc.arcstats.c` +L2_SIZE=`$SYS kstat.zfs.misc.arcstats.l2_size` +L2_HDR_SIZE=`$SYS kstat.zfs.misc.arcstats.l2_hdr_size` # # Calculation macros @@ -103,7 +105,6 @@ fi efficiency() { - if [ "$1" = "config" ]; then echo 'graph_title ZFS ARC Efficiency' echo 'graph_args -u 100' @@ -179,7 +180,6 @@ cachehitdtype() { } dmuprefetch() { - if [ "$1" = "config" ]; then echo 'graph_title ZFS DMU prefetch stats' echo 'graph_args -u 100' @@ -197,7 +197,6 @@ dmuprefetch() { } utilization() { - if [ "$1" = "config" ]; then echo 'graph_title ZFS ARC Size' echo 'graph_args --base 1024 -l 0 --vertical-label Bytes --upper-limit '$MEMMAX @@ -228,6 +227,25 @@ utilization() { fi } +l2utilization() { + if [ "$1" = "config" ]; then + echo 'graph_title ZFS L2ARC Size' + echo 'graph_args --base 1024 -r -l 0 --vertical-label Bytes' + echo 'graph_vlabel Size in MB' + echo 'graph_info This graph shows the L2ARC Size utilization' + + echo 'size.label Size' + echo 'size.draw AREA' + echo 'hdr_size.label Header Size' + echo 'hdr_size.draw AREA' + + exit 0 + else + echo 'size.value ' $L2_SIZE + echo 'hdr_size.value ' $L2_HDR_SIZE + fi +} + [ "$1" = "config" ] && echo "graph_category zfs" case "$FUNCTION" in @@ -246,4 +264,7 @@ case "$FUNCTION" in utilization) utilization $1 ;; + l2utilization) + l2utilization $1 + ;; esac From 55ba113d2f40c570ec9c7b8b750f8d8e6a895815 Mon Sep 17 00:00:00 2001 From: David Bjornsson Date: Fri, 24 Feb 2012 14:36:50 +0000 Subject: [PATCH 093/123] Added a L2ARC efficiency function, also some label consistency changes --- plugins/zfs/zfs_stats_ | 44 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/plugins/zfs/zfs_stats_ b/plugins/zfs/zfs_stats_ index 81265ecd..64d5657a 100755 --- a/plugins/zfs/zfs_stats_ +++ b/plugins/zfs/zfs_stats_ @@ -18,6 +18,8 @@ # cachehitdtype - Cache hit by data type # dmuprefetch - DMU prefetch # utilization - ARC size breakdown +# l2utilization - L2ARC size breakdown +# l2efficiency - L2ARC efficiency # #%# family=auto @@ -61,6 +63,12 @@ TARGET_SIZE=`$SYS kstat.zfs.misc.arcstats.c` L2_SIZE=`$SYS kstat.zfs.misc.arcstats.l2_size` L2_HDR_SIZE=`$SYS kstat.zfs.misc.arcstats.l2_hdr_size` +<<<<<<< HEAD + +L2_HITS=`$SYS kstat.zfs.misc.arcstats.l2_hits` +L2_MISSES=`$SYS kstat.zfs.misc.arcstats.l2_misses` +======= +>>>>>>> 20cc0e049134bf8869a57163bd54672ea279b99f # # Calculation macros @@ -103,6 +111,9 @@ else MFU_SIZE=`echo "$TARGET_SIZE-$MRU_SIZE" | $BC` fi +L2_ACCESSES_TOTAL=`echo "$L2_HITS+$L2_MISSES" | $BC` +L2_HIT_RATIO_PERC=`echo "scale=2 ; (100*$L2_HITS/$L2_ACCESSES_TOTAL)" | $BC` +L2_MISS_RATIO_PERC=`echo "scale=2 ; (100*$L2_MISSES/$L2_ACCESSES_TOTAL)" | $BC` efficiency() { if [ "$1" = "config" ]; then @@ -111,8 +122,8 @@ efficiency() { echo 'graph_vlabel %' echo 'graph_info This graph shows the ARC Efficiency' - echo 'hits.label Cache Hit Ratio' - echo 'misses.label Cache Miss Ratio' + echo 'hits.label Hit Ratio' + echo 'misses.label Miss Ratio' echo 'actual_hits.label Actual Hit Ratio' echo 'data_demand_efficiency.label Data Demand Efficiency' echo 'data_prefetch_efficiency.label Data Prefetch Efficiency' @@ -186,8 +197,8 @@ dmuprefetch() { echo 'graph_vlabel %' echo 'graph_info This graph shows the DMU prefetch stats' - echo 'hits.label Hit percentage' - echo 'misses.label Miss percentage' + echo 'hits.label Hit Ratio' + echo 'misses.label Miss Ratio' exit 0 else @@ -246,6 +257,25 @@ l2utilization() { fi } +<<<<<<< HEAD +l2efficiency() { + if [ "$1" = "config" ]; then + echo 'graph_title ZFS L2ARC Efficiency' + echo 'graph_args -u 100' + echo 'graph_vlabel %' + echo 'graph_info This graph shows the L2ARC Efficiency' + + echo 'l2_hits.label Hit Ratio' + echo 'l2_misses.label Miss Ratio' + else + echo 'l2_hits.value ' $L2_HIT_RATIO_PERC + echo 'l2_misses.value ' $L2_MISS_RATIO_PERC + fi + +} + +======= +>>>>>>> 20cc0e049134bf8869a57163bd54672ea279b99f [ "$1" = "config" ] && echo "graph_category zfs" case "$FUNCTION" in @@ -267,4 +297,10 @@ case "$FUNCTION" in l2utilization) l2utilization $1 ;; +<<<<<<< HEAD + l2efficiency) + l2efficiency $1 + ;; +======= +>>>>>>> 20cc0e049134bf8869a57163bd54672ea279b99f esac From 16174878027ef15d92395c83e4640ab4153ee507 Mon Sep 17 00:00:00 2001 From: David Bjornsson Date: Fri, 24 Feb 2012 14:42:21 +0000 Subject: [PATCH 094/123] Revert "Added a L2ARC efficiency function, also some label consistency changes" This reverts commit 55ba113d2f40c570ec9c7b8b750f8d8e6a895815. --- plugins/zfs/zfs_stats_ | 44 ++++-------------------------------------- 1 file changed, 4 insertions(+), 40 deletions(-) diff --git a/plugins/zfs/zfs_stats_ b/plugins/zfs/zfs_stats_ index 64d5657a..81265ecd 100755 --- a/plugins/zfs/zfs_stats_ +++ b/plugins/zfs/zfs_stats_ @@ -18,8 +18,6 @@ # cachehitdtype - Cache hit by data type # dmuprefetch - DMU prefetch # utilization - ARC size breakdown -# l2utilization - L2ARC size breakdown -# l2efficiency - L2ARC efficiency # #%# family=auto @@ -63,12 +61,6 @@ TARGET_SIZE=`$SYS kstat.zfs.misc.arcstats.c` L2_SIZE=`$SYS kstat.zfs.misc.arcstats.l2_size` L2_HDR_SIZE=`$SYS kstat.zfs.misc.arcstats.l2_hdr_size` -<<<<<<< HEAD - -L2_HITS=`$SYS kstat.zfs.misc.arcstats.l2_hits` -L2_MISSES=`$SYS kstat.zfs.misc.arcstats.l2_misses` -======= ->>>>>>> 20cc0e049134bf8869a57163bd54672ea279b99f # # Calculation macros @@ -111,9 +103,6 @@ else MFU_SIZE=`echo "$TARGET_SIZE-$MRU_SIZE" | $BC` fi -L2_ACCESSES_TOTAL=`echo "$L2_HITS+$L2_MISSES" | $BC` -L2_HIT_RATIO_PERC=`echo "scale=2 ; (100*$L2_HITS/$L2_ACCESSES_TOTAL)" | $BC` -L2_MISS_RATIO_PERC=`echo "scale=2 ; (100*$L2_MISSES/$L2_ACCESSES_TOTAL)" | $BC` efficiency() { if [ "$1" = "config" ]; then @@ -122,8 +111,8 @@ efficiency() { echo 'graph_vlabel %' echo 'graph_info This graph shows the ARC Efficiency' - echo 'hits.label Hit Ratio' - echo 'misses.label Miss Ratio' + echo 'hits.label Cache Hit Ratio' + echo 'misses.label Cache Miss Ratio' echo 'actual_hits.label Actual Hit Ratio' echo 'data_demand_efficiency.label Data Demand Efficiency' echo 'data_prefetch_efficiency.label Data Prefetch Efficiency' @@ -197,8 +186,8 @@ dmuprefetch() { echo 'graph_vlabel %' echo 'graph_info This graph shows the DMU prefetch stats' - echo 'hits.label Hit Ratio' - echo 'misses.label Miss Ratio' + echo 'hits.label Hit percentage' + echo 'misses.label Miss percentage' exit 0 else @@ -257,25 +246,6 @@ l2utilization() { fi } -<<<<<<< HEAD -l2efficiency() { - if [ "$1" = "config" ]; then - echo 'graph_title ZFS L2ARC Efficiency' - echo 'graph_args -u 100' - echo 'graph_vlabel %' - echo 'graph_info This graph shows the L2ARC Efficiency' - - echo 'l2_hits.label Hit Ratio' - echo 'l2_misses.label Miss Ratio' - else - echo 'l2_hits.value ' $L2_HIT_RATIO_PERC - echo 'l2_misses.value ' $L2_MISS_RATIO_PERC - fi - -} - -======= ->>>>>>> 20cc0e049134bf8869a57163bd54672ea279b99f [ "$1" = "config" ] && echo "graph_category zfs" case "$FUNCTION" in @@ -297,10 +267,4 @@ case "$FUNCTION" in l2utilization) l2utilization $1 ;; -<<<<<<< HEAD - l2efficiency) - l2efficiency $1 - ;; -======= ->>>>>>> 20cc0e049134bf8869a57163bd54672ea279b99f esac From cbe45534113dcc742115bc8a68daaa2aeb18b79a Mon Sep 17 00:00:00 2001 From: David Bjornsson Date: Fri, 24 Feb 2012 14:45:26 +0000 Subject: [PATCH 095/123] Added a L2ARC efficiency function, also some label consistency changes --- plugins/zfs/zfs_stats_ | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/plugins/zfs/zfs_stats_ b/plugins/zfs/zfs_stats_ index 81265ecd..a9d9801e 100755 --- a/plugins/zfs/zfs_stats_ +++ b/plugins/zfs/zfs_stats_ @@ -18,6 +18,8 @@ # cachehitdtype - Cache hit by data type # dmuprefetch - DMU prefetch # utilization - ARC size breakdown +# l2utilization - L2ARC size breakdown +# l2efficiency - L2ARC efficiency # #%# family=auto @@ -62,6 +64,9 @@ TARGET_SIZE=`$SYS kstat.zfs.misc.arcstats.c` L2_SIZE=`$SYS kstat.zfs.misc.arcstats.l2_size` L2_HDR_SIZE=`$SYS kstat.zfs.misc.arcstats.l2_hdr_size` +L2_HITS=`$SYS kstat.zfs.misc.arcstats.l2_hits` +L2_MISSES=`$SYS kstat.zfs.misc.arcstats.l2_misses` + # # Calculation macros # @@ -103,6 +108,9 @@ else MFU_SIZE=`echo "$TARGET_SIZE-$MRU_SIZE" | $BC` fi +L2_ACCESSES_TOTAL=`echo "$L2_HITS+$L2_MISSES" | $BC` +L2_HIT_RATIO_PERC=`echo "scale=2 ; (100*$L2_HITS/$L2_ACCESSES_TOTAL)" | $BC` +L2_MISS_RATIO_PERC=`echo "scale=2 ; (100*$L2_MISSES/$L2_ACCESSES_TOTAL)" | $BC` efficiency() { if [ "$1" = "config" ]; then @@ -111,8 +119,8 @@ efficiency() { echo 'graph_vlabel %' echo 'graph_info This graph shows the ARC Efficiency' - echo 'hits.label Cache Hit Ratio' - echo 'misses.label Cache Miss Ratio' + echo 'hits.label Hit Ratio' + echo 'misses.label Miss Ratio' echo 'actual_hits.label Actual Hit Ratio' echo 'data_demand_efficiency.label Data Demand Efficiency' echo 'data_prefetch_efficiency.label Data Prefetch Efficiency' @@ -186,8 +194,8 @@ dmuprefetch() { echo 'graph_vlabel %' echo 'graph_info This graph shows the DMU prefetch stats' - echo 'hits.label Hit percentage' - echo 'misses.label Miss percentage' + echo 'hits.label Hit Ratio' + echo 'misses.label Miss Ratio' exit 0 else @@ -246,6 +254,22 @@ l2utilization() { fi } +l2efficiency() { + if [ "$1" = "config" ]; then + echo 'graph_title ZFS L2ARC Efficiency' + echo 'graph_args -u 100' + echo 'graph_vlabel %' + echo 'graph_info This graph shows the L2ARC Efficiency' + + echo 'l2_hits.label Hit Ratio' + echo 'l2_misses.label Miss Ratio' + else + echo 'l2_hits.value ' $L2_HIT_RATIO_PERC + echo 'l2_misses.value ' $L2_MISS_RATIO_PERC + fi + +} + [ "$1" = "config" ] && echo "graph_category zfs" case "$FUNCTION" in @@ -267,4 +291,7 @@ case "$FUNCTION" in l2utilization) l2utilization $1 ;; + l2efficiency) + l2efficiency $1 + ;; esac From f14628ad35d567fb1cc2ff2cdd0fd64f13348b96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=93=D0=BE=D1=80=D0=BB=D0=BE=D0=B2=20=D0=9C=D0=B0=D0=BA?= =?UTF-8?q?=D1=81=D0=B8=D0=BC?= Date: Sat, 25 Feb 2012 12:22:25 +0400 Subject: [PATCH 096/123] Changed the location of their own plug-ins --- plugins/{other => network}/if | 0 plugins/{other => power}/batteries | 0 plugins/{other => squid}/squid | 0 plugins/{other => system}/cpu | 0 plugins/{other => system}/irq | 0 plugins/{other => system}/meminfo | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename plugins/{other => network}/if (100%) rename plugins/{other => power}/batteries (100%) rename plugins/{other => squid}/squid (100%) rename plugins/{other => system}/cpu (100%) rename plugins/{other => system}/irq (100%) rename plugins/{other => system}/meminfo (100%) diff --git a/plugins/other/if b/plugins/network/if similarity index 100% rename from plugins/other/if rename to plugins/network/if diff --git a/plugins/other/batteries b/plugins/power/batteries similarity index 100% rename from plugins/other/batteries rename to plugins/power/batteries diff --git a/plugins/other/squid b/plugins/squid/squid similarity index 100% rename from plugins/other/squid rename to plugins/squid/squid diff --git a/plugins/other/cpu b/plugins/system/cpu similarity index 100% rename from plugins/other/cpu rename to plugins/system/cpu diff --git a/plugins/other/irq b/plugins/system/irq similarity index 100% rename from plugins/other/irq rename to plugins/system/irq diff --git a/plugins/other/meminfo b/plugins/system/meminfo similarity index 100% rename from plugins/other/meminfo rename to plugins/system/meminfo From b354f28110a174c67084de0726673bd9ffe28467 Mon Sep 17 00:00:00 2001 From: Andrey Kozhokaru Date: Sat, 25 Feb 2012 19:13:56 +0200 Subject: [PATCH 097/123] Add RackSpace monitoring plugins --- plugins/rackspace/README | 8 +++ plugins/rackspace/rackspace_cdn_count.php | 70 +++++++++++++++++++++++ plugins/rackspace/rackspace_cdn_size.php | 67 ++++++++++++++++++++++ 3 files changed, 145 insertions(+) create mode 100644 plugins/rackspace/README create mode 100644 plugins/rackspace/rackspace_cdn_count.php create mode 100644 plugins/rackspace/rackspace_cdn_size.php diff --git a/plugins/rackspace/README b/plugins/rackspace/README new file mode 100644 index 00000000..9faf8c8d --- /dev/null +++ b/plugins/rackspace/README @@ -0,0 +1,8 @@ +====================================================================================== +These plugins are made to monitor RackSpace Cloudfiles storage usage and files +count. + +====================================================================================== + +Andrey Kozhokaru +andrey@kozhokaru.com diff --git a/plugins/rackspace/rackspace_cdn_count.php b/plugins/rackspace/rackspace_cdn_count.php new file mode 100644 index 00000000..71356e2d --- /dev/null +++ b/plugins/rackspace/rackspace_cdn_count.php @@ -0,0 +1,70 @@ +#!/usr/bin/php +# Author Andrey Kozhokaru +# Plugin to monitor Rackspace File count +# +# Parameters: +# +# config (required) +# +# +#%# family=manual + + + diff --git a/plugins/rackspace/rackspace_cdn_size.php b/plugins/rackspace/rackspace_cdn_size.php new file mode 100644 index 00000000..45fca542 --- /dev/null +++ b/plugins/rackspace/rackspace_cdn_size.php @@ -0,0 +1,67 @@ +#!/usr/bin/php +# Author Andrey Kozhokaru +# Plugin to monitor Rackspace CloudFile storage usage +# +# Parameters: +# +# config (required) +# +# +#%# family=manual + + From 0aa2b8d1f542149e8f0efce0f253014f3bf7bbe3 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sat, 25 Feb 2012 22:43:08 +0100 Subject: [PATCH 098/123] measure IMAP bandwidth based on mbsync/isync --- plugins/mail/imap_bandwidth | 212 ++++++++++++++++++++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100755 plugins/mail/imap_bandwidth diff --git a/plugins/mail/imap_bandwidth b/plugins/mail/imap_bandwidth new file mode 100755 index 00000000..21fde171 --- /dev/null +++ b/plugins/mail/imap_bandwidth @@ -0,0 +1,212 @@ +#!/bin/sh + +: <<=cut +=head1 NAME + +imap_bandwidth - Munin plugin to measure the current bandwidth of one or more remote IMAP servers. + +=head1 APPLICABLE SYSTEMS + +Any Linux system with the package "isync" (or "mbsync") installed. + +=head1 CONFIGURATION + +This configuration section shows a usable example for two imap servers: + [imap_bandwidth] + env.imap_servers internal=imap-internal.example.org external=imap.example.org + env.cert_file /etc/munin-imap-cert.pem + env.username foo_user + env.password secret + env.transfer_volume_kbyte 100 + env.use_ssl yes + + +Both "use_ssl" and "transfer_volume_kbyte" are optional and default to the above +values. +All other parameters are required. + +Generate the certificate file by running "mbsync-get-cert imap.example.org". + +"imap_servers" is a space-separated list of key=value combinations. "key" is +used as a label in munin graphs. "value" is the full hostname or IP of the IMAP +server. All IMAP servers need to share the same authentication database (i.e. +accept the same username/password). + +Reduce the "transfer_volume_kbyte" parameter if you need to minimize traffic. + +Maybe you need to specify the "timeout" setting for this plugin if it takes +longer than munin's default timeout. + + +=head1 INTERPRETATION + +The plugin simply shows the average bandwidth during one IMAP upload and one +IMAP download of a file containg random bytes. + +You need to be aware that this measurement obviously increases the load on +your IMAP servers for the duration of the measurement. + +You also need to be aware of the safety implications imposed by storing +sensitive information (username and password combinations) on your monitoring +server in plaintext. + +=head1 VERSION + +Version 1.0 + +=head1 BUGS + +None known + +Set the environment variable DEBUG=1 if you need to investigate problems. + +=head1 AUTHOR + +Lars Kruse + +=head1 LICENSE + +GPLv3 or higher + +=cut + + +set -eu + +#%# family=auto +#%# capabilities=autoconf + +TRANSFER_SIZE=${transfer_volume_kbyte:-100} +USE_SSL=${use_ssl:-yes} +IMAP_USERNAME=${username} +IMAP_PASSWORD=${password} +CERT_FILE=${cert_file} + +# example value: +# internal=imap-internal.example.org external=imap.example.org +SERVERS="$imap_servers" + + +if test -n "${DEBUG:-}"; then + TRANSFER_SIZE=20 + set -x +fi + + +. $MUNIN_LIBDIR/plugins/plugin.sh + + +if [ "$1" = "autoconf" ]; then + if ( which mbsync >/dev/null 2>&1 ); then + echo yes + exit 0 + else + echo "no (could not run \"mbsync\")" + exit 0 + fi +fi + +if [ "$1" = "config" ]; then + echo 'graph_title IMAP speed' + echo 'graph_vlabel upload/download speed [bit/s]' + echo 'graph_category network' + for item in $SERVERS; do + key="$(echo "$item" | cut -f 1 -d =)" + clean_name="$(clean_fieldname "$key")" + echo "download_${clean_name}.graph no" + echo "upload_${clean_name}.label $key" + echo "upload_${clean_name}.negative download_${clean_name}" + done + exit 0 +fi + + +create_dummy_file() { + dd if=/dev/urandom "of=$DUMMY_FILENAME" bs=1K count=${TRANSFER_SIZE} 2>/dev/null +} + +is_dummy_file_missing() { + test ! -e "$DUMMY_FILENAME" +} + +remove_mail_files() { + get_mail_files | while read fname; do rm "$fname"; done +} + +get_mail_files() { + find "$MAILDIR" -type f | grep -v "/\." +} + +# run the synchronization +run_sync() { + if test -n "${DEBUG:-}"; then + echo yes | mbsync --config "$SYNCRC" sync || true + else + echo yes | mbsync --config "$SYNCRC" sync >/dev/null 2>/dev/null || true + fi +} + +# run the synchronization and determine the duration of this operation +speed_sync() { + start=$(date +%s%N) + run_sync + end=$(date +%s%N) + # did we wrap a minute? + test "$end" -lt "$start" && end=$((end + 60 * 1000000000)) + delay=$((end - start)) + # use "bit" multiplier + echo "$((8 * TRANSFER_SIZE * 1024 * 1000000000 / delay))" +} + +for item in $SERVERS; do + key="$(echo "$item" | cut -f 1 -d =)" + host="$(echo "$item" | cut -f 2- -d =)" + clean_name="$(clean_fieldname "$key")" + MAILDIR="$(mktemp -d)" + # this file needs to include a dot at the beginning - otherwise it gets cleaned up ... + SYNCRC="$MAILDIR/.mbsyncrc" + SYNC_STATE_FILE_PREFIX="$MAILDIR/.syncstate-" + + cat - >"$SYNCRC" <<- EOF + SyncState $SYNC_STATE_FILE_PREFIX + Expunge Both + + MaildirStore local + Path $MAILDIR + Inbox $MAILDIR + + IMAPStore remote + Host $host + UseIMAPS $USE_SSL + User $IMAP_USERNAME + Pass $IMAP_PASSWORD + CertificateFile $CERT_FILE + + Channel sync + Master :local: + Slave :remote: +EOF + + mkdir "$MAILDIR/new" "$MAILDIR/tmp" "$MAILDIR/cur" + DUMMY_FILENAME="$MAILDIR/new/$(date +%N)" + # download all existing files + run_sync + # remove all local files -> to be purged remotely later + remove_mail_files + # create dummy file for upload + create_dummy_file "$MAILDIR" + output="upload_${clean_name}.value $(speed_sync)" + is_dummy_file_missing && echo "$output" || echo >&2 "upload failed" + # remove local file + remove_mail_files "$MAILDIR" + # persuade mbsync that we have never seen the dummy file ... + rm "$SYNC_STATE_FILE_PREFIX"* + output="download_${clean_name}.value $(speed_sync)" + get_mail_files | grep -q . && echo "$output" || echo >&2 "download failed" + # remove the new file from the imap server + remove_mail_files + run_sync + # clean up + rm -r "$MAILDIR" + done + From ea382ede4ef194c11f982c3ca3bb85a791cbad79 Mon Sep 17 00:00:00 2001 From: Lars Kruse Date: Sun, 26 Feb 2012 03:45:47 +0100 Subject: [PATCH 099/123] improved graph labels added changelog --- plugins/mail/imap_bandwidth | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/plugins/mail/imap_bandwidth b/plugins/mail/imap_bandwidth index 21fde171..fc5cd894 100755 --- a/plugins/mail/imap_bandwidth +++ b/plugins/mail/imap_bandwidth @@ -1,4 +1,11 @@ #!/bin/sh +# +# Revision 1.1 2012/02/26 03:43:27 +# Improved labels +# +# Revision 1.0 2012/02/25 21:31:16 +# Initial release +# : <<=cut =head1 NAME @@ -107,13 +114,14 @@ if [ "$1" = "autoconf" ]; then fi if [ "$1" = "config" ]; then - echo 'graph_title IMAP speed' - echo 'graph_vlabel upload/download speed [bit/s]' + echo 'graph_title IMAP bandwidth' + echo 'graph_vlabel to (+) / from (-) server [bit/s]' echo 'graph_category network' for item in $SERVERS; do key="$(echo "$item" | cut -f 1 -d =)" clean_name="$(clean_fieldname "$key")" echo "download_${clean_name}.graph no" + echo "download_${clean_name}.label download" echo "upload_${clean_name}.label $key" echo "upload_${clean_name}.negative download_${clean_name}" done From 5a41f45571652e1235d65417ea3ef6ff6d15092e Mon Sep 17 00:00:00 2001 From: Kenyon Ralph Date: Mon, 27 Feb 2012 01:44:27 -0800 Subject: [PATCH 100/123] remove obsolete dovecot plugin and move the newer one to the mail directory --- plugins/correo/dovecot126 | 168 -------------------------------- plugins/{other => mail}/dovecot | 0 2 files changed, 168 deletions(-) delete mode 100755 plugins/correo/dovecot126 rename plugins/{other => mail}/dovecot (100%) diff --git a/plugins/correo/dovecot126 b/plugins/correo/dovecot126 deleted file mode 100755 index 3a5d65bc..00000000 --- a/plugins/correo/dovecot126 +++ /dev/null @@ -1,168 +0,0 @@ -#!/bin/sh -# -# Munin Plugin -# to count logins to your dovecot mailserver -# -# Created by Dominik Schulz -# http://developer.gauner.org/munin/ -# Contributions by: -# - Stephane Enten -# Modified by Fabián Sellés Rosa -# and Arturo Blanco Paramio -# cleaned up and adapted for dovecot 1.2.6 for Ubuntu 10.4 -# Parameters understood: -# -# config (required) -# autoconf (optional - used by munin-config) -# -# Config variables: -# -# logfile - Where to find the syslog file -# -# Add the following line to a file in /etc/munin/plugin-conf.d: -# env.logfile /var/log/your/logfile.log -# -# Magic markers (optional - used by munin-config and installation scripts): -# -#%# family=auto -#%# capabilities=autoconf - -###################### -# Configuration -###################### -STAT_FILE=/var/lib/munin/plugin-state/plugin-dovecot.state -EXPR_BIN=/usr/bin/expr -LOGFILE=${logfile:-/var/log/dovecot-info.log} -###################### - -if [ "$1" = "autoconf" ]; then - echo yes - exit 0 -fi - -if [ "$1" = "config" ]; then - echo 'graph_title Logins en Dovecot' - echo 'graph_args --base 1000 -l 0' - echo 'graph_vlabel Contadores de Login' - echo 'graph_total total' - echo 'graph_category Correo' - - echo 'login_total.label Total Logins' - echo 'login_total.min 1' - - echo 'login_tls.label TLS Logins' - echo 'login_tls.min 1' - echo 'login_ssl.label SSL Logins' - echo 'login_ssl.label SSL Logins' - echo 'login_imap.label IMAP Logins' - echo 'login_pop3.label POP3 Logins' - echo 'connected.label Connected Users' - exit 0 -fi - -############################# -# Initialization -############################# -if [ ! -r $STAT_FILE ]; then - echo "TOTAL=0" > $STAT_FILE - echo "TLS=0" >> $STAT_FILE - echo "SSL=0" >> $STAT_FILE - echo "IMAP=0" >> $STAT_FILE - echo "POP3=0" >> $STAT_FILE -fi -############################# - - -###################### -# Total Logins -###################### - -NEW_TOTAL=$(egrep '*Login' $LOGFILE | grep "`date '+%Y-%m-%d'`" | sort | wc -l) -OLD_TOTAL=$(grep TOTAL $STAT_FILE | cut -f2 -d '=') -TOTAL=$(($NEW_TOTAL - $OLD_TOTAL)) -LOGINVALUE=0 -if [ $TOTAL -gt 0 ]; then - LOGINVALUE=$TOTAL -fi - -###################### -# Connected Users -###################### -DISCONNECTS=$(egrep '*Disconnected' $LOGFILE | sort | wc -l) -CONNECTS=$(egrep '.*Login' $LOGFILE | sort | wc -l) -DISCON=$(($CONNECTS - $DISCONNECTS)) -if [ $DISCON -lt 0 ]; then - DISCON=0 -fi - -###################### -# TLS Logins -###################### - -NEW_TLS=$(egrep '.*Login.*TLS' $LOGFILE | grep "`date '+%Y-%m-%d'`" | sort | wc -l) -OLD_TLS=$(grep TLS $STAT_FILE | cut -f2 -d '=') -TLS=$(($NEW_TLS - $OLD_TLS)) -TLSVALUE=0 -if [ $TLS -gt 0 ]; then - TLSVALUE=$TLS -fi -echo -n -###################### -# SSL Logins -###################### - -NEW_SSL=$(egrep '.*Login.*SSL' $LOGFILE | grep "`date '+%Y-%m-%d'`" | sort | wc -l) -OLD_SSL=$(grep SSL $STAT_FILE | cut -f2 -d '=') -SSL=$(($NEW_SSL - $OLD_SSL)) -SSLVALUE=0 -if [ $SSL -gt 0 ]; then - SSLVALUE=$SSL -fi - -###################### -# IMAP Logins -###################### - -NEW_IMAP=$(egrep '.*imap.*Login' $LOGFILE | grep "`date '+%Y-%m-%d'`" | sort | wc -l) -OLD_IMAP=$(grep IMAP $STAT_FILE | cut -f2 -d '=') -IMAP=$(($NEW_IMAP - $OLD_IMAP)) -IMAPVALUE=0 -if [ $IMAP -gt 0 ]; then - IMAPVALUE=$IMAP -fi - -###################### -# POP3 Logins -###################### - -NEW_POP3=$(egrep '.*pop3.*Login' $LOGFILE | grep "`date '+%Y-%m-%d'`" | sort | wc -l) -OLD_POP3=$(grep POP3 $STAT_FILE | cut -f2 -d '=') -POP3=$(($NEW_POP3 - $OLD_POP3)) -POP3VALUE=0 -if [ $POP3 -gt 0 ]; then - POP3VALUE=$POP3 -fi - - -####################### -# echo the new values -###################### - -echo "login_total.value $LOGINVALUE" -echo "connected.value $DISCON" -echo "login_tls.value $TLSVALUE" -echo "login_ssl.value $SSLVALUE " -echo "login_imap.value $IMAPVALUE" -echo "login_pop3.value $POP3VALUE " - - - -###################### -# Save the new values -###################### -echo "TOTAL=$NEW_TOTAL" > $STAT_FILE -echo "TLS=$NEW_TLS" >> $STAT_FILE -echo "SSL=$NEW_SSL" >> $STAT_FILE -echo "IMAP=$NEW_IMAP" >> $STAT_FILE -echo "POP3=$NEW_POP3" >> $STAT_FILE - diff --git a/plugins/other/dovecot b/plugins/mail/dovecot similarity index 100% rename from plugins/other/dovecot rename to plugins/mail/dovecot From 2584caff7881d31cdb6f51fe1257f80360439bdb Mon Sep 17 00:00:00 2001 From: Dominik Schulz Date: Sun, 26 Feb 2012 20:16:43 +0800 Subject: [PATCH 101/123] Add djabberd plugin. This commit adds a plugin to monitor DJabberd servers using the AdminPort feature which offers a telnet admin interface. It is a multi-personality plugin written in perl. --- plugins/djabberd/djabberd_ | 285 +++++++++++++++++++++++++++++++++++++ 1 file changed, 285 insertions(+) create mode 100644 plugins/djabberd/djabberd_ diff --git a/plugins/djabberd/djabberd_ b/plugins/djabberd/djabberd_ new file mode 100644 index 00000000..247af90c --- /dev/null +++ b/plugins/djabberd/djabberd_ @@ -0,0 +1,285 @@ +#!/usr/bin/perl -w +# +# Copyright (C) 2012 Dominik Schulz +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; version 2 dated June, +# 1991. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# Script to monitor DJabberd servers. +# Mostly an adaptation of Guillaume Blairon's mogliefsd_activity script. +# +# Usage: +# ln -s /usr/share/munin/plugins/djabberd_ \ +# /etc/munin/plugins/djabberd_{connections,memory,latency,counters} +# +# Configuration variables: +# +# host (default: '127.0.0.1') +# port (default: '5200') +# +# Parameters: +# +# config (required) +# autoconf (optional - only used by munin-config) +# +#%# family=auto +#%# capabilities=autoconf + +use strict; +use warnings; +use IO::Socket; + +my $djabberd_host = $ENV{host} || "127.0.0.1"; +my $djabberd_port = $ENV{port} || 5200; +my $mode = undef; +my $mode_name = undef; + +# mapping mode to command from which we get the information +my $mode_ref = { + 'connections' => { + 'cmd' => 'stats', + 'title' => 'DJabberd Connections', + 'vlabel' => 'connections', + 'fields' => { + 'connections' => { + 'key' => 'connections', + 'label' => 'Connections', + 'description' => 'Number of current connections', + 'draw' => 'LINE1', + 'type' => 'GAUGE', + }, + 'users' => { + 'key' => 'users', + 'label' => 'Users', + 'description' => 'Number of connected users', + 'draw' => 'LINE1', + 'type' => 'GAUGE', + }, + }, + }, + 'memory' => { + 'cmd' => 'stats', + 'title' => 'DJabberd Memory Statistics', + 'vlabel' => 'Bytes', + 'fields' => { + 'mem_total' => { + 'key' => 'mem_total', + 'label' => '', + 'description' => 'Total memory used by DJabberd', + 'draw' => 'LINE1', + 'type' => 'GAUGE', + 'filter' => \&kb2bytes, + }, + 'mem_connections' => { + 'key' => 'mem_connections', + 'label' => '', + 'description' => 'Memory used for handling connections', + 'draw' => 'LINE1', + 'type' => 'GAUGE', + 'filter' => \&kb2bytes, + }, + 'mem_per_connection' => { + 'key' => 'mem_per_connection', + 'label' => '', + 'description' => 'Memory used per connection', + 'draw' => 'LINE1', + 'type' => 'GAUGE', + 'filter' => \&kb2bytes, + }, + } + }, + 'latency' => { + 'cmd' => 'latency', + 'title' => 'DJabberd Latency Statistics', + 'vlabel' => 'reqs.', + 'fields' => { + 'dotzerozerozerofive' => { + 'key' => '-0.0005', + 'label' => 'lt. 0.0005', + 'description' => 'Requests handled in lt. 0.0005s', + 'draw' => 'AREA', + 'type' => 'COUNTER', + }, + 'dotzerozeroone' => { + 'key' => '-0.001', + 'label' => 'lt. 0.001', + 'description' => 'Requests handled int lt. 0.001s', + 'draw' => 'STACK', + 'type' => 'COUNTER', + }, + 'dotzerozerotwo' => { + 'key' => '-0.002', + 'label' => 'lt. 0.002', + 'description' => 'Requests handled int lt. 0.002s', + 'draw' => 'STACK', + 'type' => 'COUNTER', + }, + 'dotzerozerofive' => { + 'key' => '-0.005', + 'label' => 'lt. 0.005', + 'description' => 'Requests handled int lt. 0.005s', + 'draw' => 'STACK', + 'type' => 'COUNTER', + }, + 'dotzeroone' => { + 'key' => '-0.01', + 'label' => 'lt. 0.01', + 'description' => 'Requests handled int lt. 0.01s', + 'draw' => 'STACK', + 'type' => 'COUNTER', + }, + 'dotzerotwo' => { + 'key' => '-0.02', + 'label' => 'lt. 0.02', + 'description' => 'Requests handled int lt. 0.02s', + 'draw' => 'STACK', + 'type' => 'COUNTER', + }, + } + }, + 'counters' => { + 'cmd' => 'counters', + 'title' => 'DJabberd Counters', + 'vlabel' => 'msgs.', + 'fields' => { + 'clientin_djabberd_iq' => { 'key' => 'ClientIn:DJabberd::IQ', 'type' => 'COUNTER', }, + 'clientin_djabberd_message' => { 'key' => 'ClientIn:DJabberd::Message', 'type' => 'COUNTER', }, + 'clientin_djabberd_presence' => { 'key' => 'ClientIn:DJabberd::Presence', 'type' => 'COUNTER', }, + 'clientin_djabberd_stanza_sasl' => { 'key' => 'ClientIn:DJabberd::Stanza::SASL', 'type' => 'COUNTER', }, + 'clientin_djabberd_stanza_starttls' => { 'key' => 'ClientIn:DJabberd::Stanza::StartTLS', 'type' => 'COUNTER', }, + 'iniq_get_info_query' => { 'key' => 'InIQ:get-{http://jabber.org/protocol/disco#info}query', 'type' => 'COUNTER', }, + 'iniq_get_items_query' => { 'key' => 'InIQ:get-{http://jabber.org/protocol/disco#items}query', 'type' => 'COUNTER', }, + 'iniq_get_roster_query' => { 'key' => 'InIQ:get-{jabber:iq:roster}query', 'type' => 'COUNTER', }, + 'iniq_get_bind' => { 'key' => 'InIQ:set-{urn:ietf:params:xml:ns:xmpp-bind}bind', 'type' => 'COUNTER', }, + 'iniq_get_session' => { 'key' => 'InIQ:set-{urn:ietf:params:xml:ns:xmpp-session}session', 'type' => 'COUNTER', }, + 'serverin_djabberd_stanza_dialback_result' => { 'key' => 'ServerIn:DJabberd::Stanza::DialbackResult', 'type' => 'COUNTER', }, + 'serverin_djabberd_stanza_dialback_verify' => { 'key' => 'ServerIn:DJabberd::Stanza::DialbackVerify', 'type' => 'COUNTER', }, + 'auth_success' => { 'key' => 'auth_success', 'type' => 'COUNTER', }, + 'c2s_message' => { 'key' => 'c2s-Message', 'type' => 'COUNTER', }, + 'c2s_presence' => { 'key' => 'c2s-Presence', 'type' => 'COUNTER', }, + 'connect' => { 'key' => 'connect', 'type' => 'COUNTER', }, + 'deliver_local' => { 'key' => 'deliver_local', 'type' => 'COUNTER', }, + 'deliver_s2s' => { 'key' => 'deliver_s2s', 'type' => 'COUNTER', }, + 'disconnect' => { 'key' => 'disconnect', 'type' => 'COUNTER', }, + }, + }, +}; + +if ( $0 =~ m/djabberd_(.*)$/ && $mode_ref->{$1} ) { + $mode_name = $1; + $mode = $mode_ref->{$mode_name}; +} +else { + print STDERR "ERROR: Unknown mode '$mode'. Exiting.\n"; + exit -1; +} + +if ( $ARGV[0] && $ARGV[0] eq 'suggest' ) { + print join( "\n", keys %$mode_ref ); + + exit 0; +} +elsif ( $ARGV[0] && $ARGV[0] eq "autoconf" ) { + my $result_ref = &query_djabberd( $djabberd_host, $djabberd_port, $mode ); + + if ($result_ref) { + print "yes\n"; + } + else { + print "no\n"; + } + + exit 0; +} +elsif ( $ARGV[0] and $ARGV[0] eq "config" ) { + print "graph_title " . $mode->{'title'} . "\n"; + print "graph_vlabel " . $mode->{'vlabel'} . "\n"; + print "graph_args -l 0\n"; + print "graph_category DJabberd\n"; + foreach my $field_name ( keys %{ $mode->{'fields'} } ) { + my $label = $mode->{'fields'}->{$field_name}->{'label'} || $field_name; + my $desc = $mode->{'fields'}->{$field_name}->{'description'} || $mode->{'fields'}->{$field_name}->{'key'}; + my $draw = $mode->{'fields'}->{$field_name}->{'draw'} || 'LINE1'; + my $type = $mode->{'fields'}->{$field_name}->{'type'} || 'COUNTER'; + + print $field_name. '.label ' . $label . "\n"; + print $field_name. '.description ' . $desc . "\n"; + print $field_name. '.draw ' . $draw . "\n"; + print $field_name. '.type ' . $type . "\n"; + } + + exit 0; +} +else { + my $result_ref = &query_djabberd( $djabberd_host, $djabberd_port, $mode ); + + foreach my $field_name ( keys %{ $mode->{'fields'} } ) { + my $key = $mode->{'fields'}->{$field_name}->{'key'}; + if ( defined( $result_ref->{$key} ) ) { # check for definedness, may well be zero (false for perl) + my $value = $result_ref->{$key}; + + # if there is a filter defined for this key apply it now + if ( exists( $mode->{'fields'}->{$field_name}->{'filter'} ) && ref( $mode->{'fields'}->{$field_name}->{'filter'} ) eq 'CODE' ) { + $value = &{ $mode->{'fields'}->{$field_name}->{'filter'} }($value); + } + print $field_name. ".value " . $value . "\n"; + } + } +} + +sub query_djabberd { + my ( $host, $port, $mode ) = @_; + + my $conn = IO::Socket::INET::->new( + PeerAddr => $host, + PeerPort => $port, + Proto => 'tcp', + Timeout => 5, + ) or die($!); + + my $request = $mode->{'cmd'} . "\n"; + + $conn->syswrite( $request, length($request) ); + + my @lines = (); + while ( my $line = $conn->getline() ) { + if ( $line =~ /^\./ ) { + last; + } + push( @lines, $line ); + } + close($conn); + + my $result_ref = {}; + foreach my $line (@lines) { + my ( $key, $value, $unit ) = split /\s+/, $line; + if ( $key && $value ) { + $result_ref->{$key} = $value; + } + } + + return $result_ref; +} + +# transform kb => bytes +sub kb2bytes { + + my $num = shift; + + if ( $num && $num =~ m/^\d+$/ ) { + $num *= 1024; + } + + return $num; +} From 4f432c597c8040a92a8e0a0a0951dc674fa8b633 Mon Sep 17 00:00:00 2001 From: Benjamin Ryzman Date: Thu, 23 Feb 2012 17:20:44 +0800 Subject: [PATCH 102/123] SNMP plugin to graph Brocade NetIron temperatures per module --- plugins/network/snmp__brocade_temp_module_ | 88 ++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100755 plugins/network/snmp__brocade_temp_module_ diff --git a/plugins/network/snmp__brocade_temp_module_ b/plugins/network/snmp__brocade_temp_module_ new file mode 100755 index 00000000..9f2954e2 --- /dev/null +++ b/plugins/network/snmp__brocade_temp_module_ @@ -0,0 +1,88 @@ +#!/usr/bin/perl -w + +=head1 MAGIC MARKERS + + #%# family=snmpauto + #%# capabilities=snmpconf + +=cut + +use strict; +use Munin::Plugin; +use Munin::Plugin::SNMP; + +my $DEBUG=$ENV{'MUNIN_DEBUG'}; + +# This is the snmpwalk: +# snAgentTempSensorDescr.1.1 = STRING: "Line module 1, sensor 1 temperature" +# snAgentTempSensorDescr.1.2 = STRING: "Line module 1, sensor 2 temperature" +# snAgentTempSensorDescr.1.3 = STRING: "Line module 1, sensor 3 temperature" +# snAgentTempSensorDescr.1.4 = STRING: "Line module 1, sensor 4 temperature" +# snAgentTempSensorDescr.2.1 = STRING: "Line module 2, sensor 1 temperature" +# snAgentTempSensorDescr.2.2 = STRING: "Line module 2, sensor 2 temperature" +# snAgentTempSensorDescr.2.3 = STRING: "Line module 2, sensor 3 temperature" +# snAgentTempSensorDescr.2.4 = STRING: "Line module 2, sensor 4 temperature" +# snAgentTempSensorDescr.3.1 = STRING: "Active management module temperature" +# snAgentTempSensorDescr.3.2 = STRING: "Active management module temperature" +# snAgentTempValue.1.1 = INTEGER: 100 +# snAgentTempValue.1.2 = INTEGER: 106 +# snAgentTempValue.1.3 = INTEGER: 82 +# snAgentTempValue.1.4 = INTEGER: 72 +# snAgentTempValue.2.1 = INTEGER: 74 +# snAgentTempValue.2.2 = INTEGER: 102 +# snAgentTempValue.2.3 = INTEGER: 70 +# snAgentTempValue.2.4 = INTEGER: 74 +# snAgentTempValue.3.1 = INTEGER: 78 +# snAgentTempValue.3.2 = INTEGER: 84 + +my $brcdIp = '1.3.6.1.4.1.1991'; +my $snAgentTempTable = "$brcdIp.1.1.2.13.1"; +my $snAgentTempSensorDescr = "$snAgentTempTable.1.3"; +my $snAgentTempValue = "$snAgentTempTable.1.4"; + + +if (defined $ARGV[0] and $ARGV[0] eq "snmpconf") { + print "index $snAgentTempTable.1.3.\n"; + print "require $snAgentTempSensorDescr. [1-9]\n"; + print "require $snAgentTempValue. [1-9]\n"; + exit 0; +} + +my $module = 0; + +if ($Munin::Plugin::me =~ /_module_(\d+)$/) { + $module = $1; +} else { + die "Could not determine module number from ".$Munin::Plugin::me."\n"; +} + +my ($session,$error); + +$session = Munin::Plugin::SNMP->session(-translate => [ -nosuchinstance => undef ]); + +my $sensor = 1; +if ($ARGV[0] and $ARGV[0] eq "config") { + my ($host,undef,$version) = Munin::Plugin::SNMP->config_session(); + + print "host_name $host\n" unless $host eq 'localhost'; + print "graph_title Module $module +graph_args --base 1000 --lower-limit 0 +graph_vlabel °C +graph_category system +graph_scale no\n"; + + my $descr = undef; + while (defined ($descr = $session->get_single("$snAgentTempSensorDescr.$module.$sensor"))) { + print "sensor$sensor.label $descr\n"; + $sensor ++; + } + exit 0; +} + +my $value = undef; +while (defined ($value = $session->get_single("$snAgentTempValue.$module.$sensor"))) { + $value /= 2; + print "sensor$sensor.value $value\n"; + $sensor++; +} +# vim:ft=perl From c87e34beea0a49bfcba7cd25940c364e897b4736 Mon Sep 17 00:00:00 2001 From: Azelphur Date: Tue, 28 Feb 2012 02:58:55 +0000 Subject: [PATCH 103/123] Added game module --- plugins/game/game | 216 ++++++++++++++++++++++++++++++++++++ plugins/game/munin-game.ini | 28 +++++ 2 files changed, 244 insertions(+) create mode 100644 plugins/game/game create mode 100644 plugins/game/munin-game.ini diff --git a/plugins/game/game b/plugins/game/game new file mode 100644 index 00000000..db6f485f --- /dev/null +++ b/plugins/game/game @@ -0,0 +1,216 @@ +#!/usr/bin/php + $value) { + if ($section != 'settings') + $servers[$section] = array($value['game'], $value['address'], $value['port']); + } + + // Create a new GameQ object and pass it a list of servers + $gq = new GameQ(); + $gq->addServers($servers); + + // Set timeout from the config file + $gq->setOption('timeout', $ini_array['settings']['timeout']); + $gq->setOption('sock_count', $ini_array['settings']['sock_count']); + $gq->setOption('sock_start', $ini_array['settings']['sock_start']); + + // This filter makes sure a subset of data is always available, next to the normal data + $gq->setFilter('normalise'); + + // Send request(s) + $results = $gq->requestData(); + + return $results; +} + +// Parse command line arguments if required. +if (isset($_SERVER['argv'][1])) { + if ($_SERVER['argv'][1] == 'config') { + // Load our config.ini + $ini_array = parse_ini_file($config, true); + + // Load games.ini so we can show pretty game names + $games = parse_ini_file('gameq/GameQ/games.ini', true); + + // Query the game servers + $results = queryServers($ini_array); + // Cache the query in the state file + $fp = fopen($state, 'w+') or die("I could not open state file."); + fwrite($fp, serialize($results)); + fclose($fp); + + + // Loop through each server, printing graphs. + foreach ($results as $name => $server) { + // If sub graphs and the game total graphs are enabled, make this be a sub-graph. + if ($ini_array['settings']['show_game_total'] && $ini_array['settings']['enable_sub_graphs']) + $machine_name = "gameserver_" . $ini_array[$name]['game'] . ".$name"; + else + $machine_name = "gameserver_" . $ini_array[$name]['game'] . "_$name"; + $title = $ini_array[$name]['name'] . " players"; + $info = "The number of players connected to the " . $games[$ini_array[$name]['game']]['name'] . " server"; + printMultigraph($ini_array, $machine_name, $title, $info, $server['gq_maxplayers']); + } + + // Game total graphs. + if ($ini_array['settings']['show_game_total']) { + $game_total_max = array(); + + // Count players connected to each game + foreach ($results as $name => $server) { + if (!isset($game_total_max[$ini_array[$name]['game']])) + $game_total_max[$ini_array[$name]['game']] = $server['gq_maxplayers']; + else + $game_total_max[$ini_array[$name]['game']] += $server['gq_maxplayers']; + } + + // Print all the game total graphs. + foreach ($game_total_max as $game => $total) + { + $machine_name = "gameserver_" . $game; + $title = "Total " . $games[$game]['name'] . " players"; + $info = "Total players connected to all " . $games[$game]['name'] . " servers"; + printMultigraph($ini_array, $machine_name, $title, $info, $total); + } + } + + // Global total graph + if ($ini_array['settings']['show_global_total']) { + $total_max = 0; + // Add up all the players connected to all the servers + foreach ($results as $name => $server) { + $total_max += $server['gq_maxplayers']; + } + + printMultigraph($ini_array, "gameserver", "Total Players", "Total players connected to all servers", $total_max); + } + } + if ($_SERVER['argv'][1] == 'autoconf') { + if (!@include("gameq/GameQ.php")) + p("no (GameQ Library not found)"); + elseif (!file_exists($config)) + p("no ($config not found)"); + else + p("yes"); + } + die(); +} + +// No command line arguments, print values. + +// Load our config.ini +$ini_array = parse_ini_file($config, true); + +// Load games.ini so we can show pretty game names +$games = parse_ini_file('gameq/GameQ/games.ini', true); + +$results = unserialize(file_get_contents($state)); + +// Print individual game values +foreach ($results as $name => $server){ + if ($ini_array['settings']['show_game_total'] && $ini_array['settings']['enable_sub_graphs']) + $machine_name = "gameserver_" . $ini_array[$name]['game'] . ".$name"; + else + $machine_name = "gameserver_" . $ini_array[$name]['game'] . "_$name"; + printValue($machine_name, $server['gq_numplayers']); +} + +// Print game total values +if ($ini_array['settings']['show_game_total']) { + $game_total = array(); + foreach ($results as $name => $server) { + // Add up counts for the total graph + if (!isset($game_total_max[$ini_array[$name]['game']])) + $game_total[$ini_array[$name]['game']] = $server['gq_numplayers']; + else + $game_total[$ini_array[$name]['game']] += $server['gq_numplayers']; + } + foreach ($game_total as $game => $total) + { + $machine_name = "gameserver_" . $game; + printValue($machine_name, $total); + } +} + +// Are global totals enabled? +if ($ini_array['settings']['show_global_total']) { + $total = 0; + foreach ($results as $name => $server) { + $total += $server['gq_numplayers']; + } + printValue("gameserver", $total); +} + + +?> diff --git a/plugins/game/munin-game.ini b/plugins/game/munin-game.ini new file mode 100644 index 00000000..d03d1d0a --- /dev/null +++ b/plugins/game/munin-game.ini @@ -0,0 +1,28 @@ +[settings] +timeout = 1000 ; Timeout in ms when attempting to connect to servers +sock_count = 64 ; Maximum number of sockets that are used simultaneously +sock_start = 0 ; Start of port range to use + +; Note that changing any of the below options may change your graph names, which may reset your statistics. +show_global_total = True ; Show a graph that shows the total players connected to all servers? +show_game_total = True ; Show a graph that shows the total players connected to all servers of the same type? +enable_sub_graphs = True ; Make each game be a sub-graph of it's game total graph. + +; The following variables allow you to override the style of graphs. +; The server graphs are named gameserver_gamename.machinename, for example gameserver_tf2.myamazingserver +; The game total graphs are named gameserver_gamename, for example gameserver_tf2 +; The global total graph is named gameserver. +;graph_name_colour = 000000 ; Override the color of graph_name, for example gameserver_et.myetserver_color +;graph_name_draw = LINE1 ; Override the draw method of graph_name, for example gameserver_ + +[mytf2server] ; Machine name of the server +name = "My TF2 Server" ; Display name of the server +game = tf2 ; Game type, see GameQ/games.ini inside the GameQ library for a list of valid options. +address = 1.2.3.4 ; Server IP or hostname. +port = 27015 ; Server port + +[myetserver] +name = "My Enemy Territory Server" +game = et +address = 1.2.3.4 +port = 27960 From a9cd70c4ce5b692a1797fccb95eb9e7e87d04b3a Mon Sep 17 00:00:00 2001 From: Kenyon Ralph Date: Mon, 27 Feb 2012 23:29:24 -0800 Subject: [PATCH 104/123] move game plugin to game directory --- plugins/{game => games}/game | 0 plugins/{game => games}/munin-game.ini | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename plugins/{game => games}/game (100%) rename plugins/{game => games}/munin-game.ini (100%) diff --git a/plugins/game/game b/plugins/games/game similarity index 100% rename from plugins/game/game rename to plugins/games/game diff --git a/plugins/game/munin-game.ini b/plugins/games/munin-game.ini similarity index 100% rename from plugins/game/munin-game.ini rename to plugins/games/munin-game.ini From 8e3033a0fa903a7d92e178d17d044efe09f25162 Mon Sep 17 00:00:00 2001 From: Kenyon Ralph Date: Mon, 27 Feb 2012 23:51:52 -0800 Subject: [PATCH 105/123] remove plugins included in the main munin distribution --- plugins/system/buddyinfo | 146 ----- plugins/system/cpu | 974 ---------------------------------- plugins/system/cpu_by_process | 135 ----- plugins/system/cpufreq | 55 -- plugins/system/cpufreq-avg | 144 ----- plugins/system/cpufreq-info | 100 ---- plugins/system/cpufreq_ | 193 ------- plugins/system/cpuspeed | 34 -- plugins/system/cpuspeed2 | 45 -- plugins/system/cpuspeed_sane | 60 --- plugins/system/uptime | 50 -- plugins/system/users | 30 -- plugins/system/usersv2 | 61 --- 13 files changed, 2027 deletions(-) delete mode 100755 plugins/system/buddyinfo delete mode 100755 plugins/system/cpu delete mode 100755 plugins/system/cpu_by_process delete mode 100755 plugins/system/cpufreq delete mode 100755 plugins/system/cpufreq-avg delete mode 100755 plugins/system/cpufreq-info delete mode 100755 plugins/system/cpufreq_ delete mode 100755 plugins/system/cpuspeed delete mode 100755 plugins/system/cpuspeed2 delete mode 100755 plugins/system/cpuspeed_sane delete mode 100755 plugins/system/uptime delete mode 100755 plugins/system/users delete mode 100755 plugins/system/usersv2 diff --git a/plugins/system/buddyinfo b/plugins/system/buddyinfo deleted file mode 100755 index fe8b454d..00000000 --- a/plugins/system/buddyinfo +++ /dev/null @@ -1,146 +0,0 @@ -#! /usr/bin/perl -w - -=head1 NAME - -buddyinfo - Plugin to monitor memory fragmentation on Linux systems. - -=head1 APPLICABLE SYSTEMS - -Linux 2.6 - -=head1 CONFIGURATION - -None needed. - -=head1 INTERPRETATION - -Linux manages virtual memory on a page granularity. There are some operations -however that require physically contiguous pages to be allocated by the kernel. -Such allocations may fail if the memory gets fragmented and even though there -are enough pages free, but they are not contiguous. - -This plugin monitors the amount of contiguous areas, called higher order pages. -The order means the exponent of two of the size of the area, so order 2 means -2^2 = 4 pages. - -=head1 SEE ALSO - -See C in the Linux source tree for the -description of the buddyinfo file. - -=head1 MAGIC MARKERS - - #%# family=manual - #%# capabilities=autoconf - -=head1 AUTHOR - -Gábor Gombás - -=head1 LICENSE - -GPLv2 or later - -=cut - -use strict; -use Munin::Plugin; -use POSIX; -use Carp; - -need_multigraph(); - -if ($ARGV[0] and $ARGV[0] eq 'autoconf') { - if (-f "/proc/buddyinfo") { - print "yes\n"; - } - else { - print "no (/proc/buddyinfo is missing)\n"; - } - exit 0; -} - -# The most common page size is 4k, but it is not universal -my $pagesize = POSIX::sysconf(&POSIX::_SC_PAGESIZE) / 1024; - -my $zones = {}; - -open(FH, '< /proc/buddyinfo') - or croak "Failed to open '/proc/buddyinfo': $!"; -while (my $line = ) { - chomp $line; - - $line =~ m/Node (\d+), zone\s+(\S+)\s+(\S.*)$/; - my $name = "Node $1, zone $2"; - my @cnt = split(/ +/, $3); - $zones->{$name} = \@cnt; -} -close FH; - -my $totals = []; -foreach my $zone (keys %$zones) { - for my $i (0 .. $#{$zones->{$zone}}) { - $totals->[$i] += $zones->{$zone}->[$i] - } -} - -sub do_config { - print "multigraph buddyinfo\n"; - print "graph_title Memory fragmentation\n"; - print "graph_args --base 1024 --lower-limit 0\n"; - print "graph_vlabel pages free\n"; - print "graph_category system\n"; - print "graph_info This graph shows the number of free pages of different size\n"; - print "graph_order " . join(' ', map { "order$_" } (0 .. $#{$totals})) . "\n"; - for my $i (0 .. $#{$totals}) { - print "order$i.label Order $i\n"; - print "order$i.info Number of order $i (" . ($pagesize * 2 ** $i) . " KiB) pages\n"; - print "order$i.type GAUGE\n"; - print "order$i.draw LINE2\n"; - print "order$i.min 0\n"; - } - for my $zone (sort keys %$zones) { - my $zoneid = $zone; - $zoneid = clean_fieldname($zone); - - print "multigraph buddyinfo.$zoneid\n"; - print "graph_title Memory fragmentation in $zone\n"; - print "graph_args --base 1024 --lower-limit 0\n"; - print "graph_vlabel pages free\n"; - print "graph_category system\n"; - print "graph_info This graph shows the number of free pages in $zone\n"; - print "graph_order " . - join(' ', map { "order$_" } (0 .. $#{$zones->{$zone}})) . "\n"; - for my $i (0 .. $#{$zones->{$zone}}) { - print "order$i.label Order $i\n"; - print "order$i.info Number of order $i (" . - ($pagesize * 2 ** $i) . " KiB) pages\n"; - print "order$i.type GAUGE\n"; - print "order$i.draw LINE2\n"; - print "order$i.min 0\n"; - } - } -} - -sub do_fetch { - print "multigraph buddyinfo\n"; - for my $i (0 .. $#{$totals}) { - print "order$i.value " . $totals->[$i] . "\n"; - } - for my $zone (sort keys %$zones) { - my $zoneid = $zone; - $zoneid =~ tr/ ,/__/; - - print "multigraph buddyinfo.$zoneid\n"; - for my $i (0 .. $#{$zones->{$zone}}) { - print "order$i.value " . $zones->{$zone}->[$i] . "\n"; - } - } -} - -if ($ARGV[0] and $ARGV[0] eq 'config') { - do_config(); - exit 0; -} - -do_fetch(); diff --git a/plugins/system/cpu b/plugins/system/cpu deleted file mode 100755 index 20d43dcf..00000000 --- a/plugins/system/cpu +++ /dev/null @@ -1,974 +0,0 @@ -#!/usr/bin/perl -w -# -*- perl -*- - -=head1 NAME - -cpu - Plugin to monitor CPU usage and frequencies. - -=head1 APPLICABLE SYSTEMS - -All Linux systems, but read below section - -=head1 CONFIGURATION - -The plugin automatically selects which graphics drawing. -Charts related to the frequencies of processors depends on the settings of the kernel: -Power management and ACPI options -> CPU Frequency scaling -> CPU frequency translation statistics -> Advanced statistics - -=head2 WARNING AND CRITICAL SETTINGS - -You can set warning and critical levels for each of the data -series the plugin reports. The following environment variables are -used as default for all fields: - - env.warning - env.critical - -But each field (system user nice etc...) can be controlled separately: -For example: - - env.system_warning 70 - env.user_warning 70 - env.idle_critical 1 - -Also each field of each cpu can be controlled separately -For example: - - env.cpu1_system_warning 70 - env.cpu0_user_warning 70 - env.cpu0_idle_critical 1 - -Algoritm is easy: for example current graph limit is env.cpu0_idle_critical if defined env.cpu0_idle_critical or env.idle_critical if defined env.idle_critical -or env.critical if defined env.critical. Or no limit - -=head1 INTERPRETATION - -The plugin shows each cpu usage in percent, shows the CPU frequency, -frequency shift frequencies, the percentage of use of frequencies - -=head1 MAGIC MARKERS - - #%# family=auto - #%# capabilities=autoconf - - -=head1 VERSION - - 2.0 - -=head1 BUGS - -none known - -=head1 AUTHOR - -Gorlow Maxim aka Sheridan (email and jabber) - -=head1 LICENSE - -GPLv2 - -=cut - -use strict; -use warnings; -use Munin::Plugin; -use Data::Dumper; -my @cnames = qw(user nice system idle iowait irq softirq steal guest); -my $stat_file = "/proc/stat"; -my $freq_path = "/sys/devices/system/cpu"; -my $limits = {}; -my $cpuinfo = {}; $cpuinfo->{'cpu_count'} = 0; -my @stat_file_content = (); -my $freq_mul = 1000; # CPU frequency multiplier from kHz to Hz - - - -my $graphs = -{ - 'cpu_utilisation' => # multigraph - { - 'title' => 'CPU:t: utilisation', - 'args' => '--base 1000 -r --lower-limit 0 --upper-limit 100', - 'vlabel' => '%', - 'scale' => 'no', - 'info' => 'This graph shows how CPU:t: time is spent :i:' - }, - 'cpu_all' => # single - { - 'title' => 'All CPU utilisation', - 'args' => '--base 1000 -r --lower-limit 0 --upper-limit 100', - 'vlabel' => '%', - 'scale' => 'no', - 'info' => 'This graph shows how CPU time is spent on each processor', - 'category' => 'cpu' - }, - 'cpu_freq_trans' => # multi - { - 'title' => 'CPU frequency transitions', - 'args' => '--base 1000', - 'vlabel' => 'count', - 'scale' => 'no', - 'info' => 'This graph shows CPU transitions of each processor', - }, - 'cpu_freq' => # child of cpu_freq_trans - { - 'title' => 'CPU:t: frequency (total)', - 'args' => '--base 1000 -r --lower-limit 0 --upper-limit 100', - 'vlabel' => '% of total', - 'info' => 'This graph shows CPU:t: frequency :i:', - 'category' => 'cpu' - }, - 'cpu_freq_ps' => # child of cpu_freq_trans - { - 'title' => 'CPU:t: frequency (per secund)', - 'args' => '--base 1000 -r --lower-limit 0 --upper-limit 100', - 'vlabel' => '% per secund', - 'info' => 'This graph shows CPU:t: frequency per secund from last update :i:', - 'category' => 'cpu' - }, - 'cpu_freq_trans_table' => # child of cpu_freq_trans - { - 'title' => 'CPU:t: frequency switches (total)', - 'args' => '--base 1000 -r --lower-limit 0 --upper-limit 100', - 'vlabel' => '% of total', - 'scale' => 'no', - 'info' => 'This graph shows CPU:t: frequency switches :i:', - }, - 'cpu_freq_trans_table_ps' => # child of cpu_freq_trans - { - 'title' => 'CPU:t: frequency switches (per secund)', - 'args' => '--base 1000 -r --lower-limit 0 --upper-limit 100', - 'vlabel' => '% per secund', - 'scale' => 'no', - 'info' => 'This graph shows CPU:t: frequency switches per secund from last update :i:', - } -}; - -my $transparent = 'CC'; -my $fields = -{ - 'user' => - { - 'label' => 'User', - 'info' => 'Normal processes executing in user mode', - 'type' => 'GAUGE', - 'draw' => 'AREASTACK', - 'min' => 0, - 'max' => 100 - }, - 'nice' => - { - 'label' => 'Nice', - 'info' => 'Niced processes executing in user mode', - 'type' => 'GAUGE', - 'draw' => 'AREASTACK', - 'min' => 0, - 'max' => 100 - }, - 'system' => - { - 'label' => 'System', - 'info' => 'Processes executing in kernel mode', - 'type' => 'GAUGE', - 'draw' => 'AREASTACK', - 'min' => 0, - 'max' => 100 - }, - 'idle' => - { - 'label' => 'Idle', - 'info' => 'Twiddling thumbs', - 'type' => 'GAUGE', - 'draw' => 'AREASTACK', - 'colour'=> 'FFFFDD'.$transparent, - 'min' => 0, - 'max' => 100 - }, - 'iowait' => - { - 'label' => 'I/O wait', - 'info' => 'Waiting for I/O to complete', - 'type' => 'GAUGE', - 'draw' => 'AREASTACK', - 'min' => 0, - 'max' => 100 - }, - 'irq' => - { - 'label' => 'IRQ', - 'info' => 'Servicing interrupts', - 'type' => 'GAUGE', - 'draw' => 'AREASTACK', - 'min' => 0, - 'max' => 100 - }, - 'softirq' => - { - 'label' => 'Software IRQ', - 'info' => 'Servicing software interrupts', - 'type' => 'GAUGE', - 'draw' => 'AREASTACK', - 'min' => 0, - 'max' => 100 - }, - 'steal' => - { - 'label' => 'Steal', - 'info' => 'Involuntary wait', - 'type' => 'GAUGE', - 'draw' => 'AREASTACK', - 'min' => 0, - 'max' => 100 - }, - 'guest' => - { - 'label' => 'Guest', - 'info' => 'Running a guest', - 'type' => 'GAUGE', - 'draw' => 'AREASTACK', - 'min' => 0, - 'max' => 100 - }, - 'cpu_util' => - { - 'label' => ':t:', - 'info' => ':t: utilisation', - 'type' => 'GAUGE', - 'draw' => 'LINE0.5', - 'min' => 0, - 'max' => 100 - }, - 'freq_percent' => - { - 'label' => 'CPU:t: frequency', - 'info' => 'CPU:t: frequency percent', - 'type' => 'GAUGE', - 'draw' => 'LINE0.5', - 'min' => 0, - 'max' => 100 - }, - 'freq_hz' => - { - 'label' => ':t:', - 'info' => 'CPU :t: frequency', - 'type' => 'GAUGE', - 'draw' => 'AREASTACK', - 'min' => 0, - 'max' => 100 - }, - 'freq_trans' => - { - 'label' => ':t:', - 'info' => ':t: frequency transitions', - 'type' => 'GAUGE', - 'draw' => 'LINE0.5', - 'min' => 0 - }, - 'freq_trans_table' => - { - 'label' => ':t:', - 'info' => ':t: frequency switch', - 'type' => 'GAUGE', - 'draw' => 'AREASTACK', - 'min' => 0, - 'max' => 100 - } - -}; - - - -# ----------------- main ---------------- -load_cpuinfo(); -need_multigraph(); -remove_unavialabled_counters(); - -if (defined($ARGV[0]) and ($ARGV[0] eq 'autoconf')) -{ - printf("%s\n", -e $stat_file ? "yes" : "no (stats not exists)"); - exit (0); -} - -if (defined($ARGV[0]) and ($ARGV[0] eq 'config')) -{ - print_config(); - exit (0); -} - -print_values(); -#print Dumper prepare_graphs_fields(); -exit(0); - -# ----------------- sub's ---------------- - -# ----------------------- trim whitespace at begin and end of string ------------ -sub trim -{ - my($string)=@_; - for ($string) { s/^\s+//; s/\s+$//; } - return $string; -} - - -my $items_exists = {}; -sub check_exists -{ - my ($t, $cpu_num) = @_[0..1]; - if (defined($cpu_num)) - { - unless (exists($items_exists->{$t}{$cpu_num})) - { - if ($t eq 'freq_hz') { $items_exists->{$t}{$cpu_num} = ( -e sprintf("%s/cpu%s/cpufreq/scaling_min_freq" , $freq_path, $cpu_num) and - -e sprintf("%s/cpu%s/cpufreq/scaling_cur_freq" , $freq_path, $cpu_num) and - -e sprintf("%s/cpu%s/cpufreq/scaling_max_freq" , $freq_path, $cpu_num)); } - elsif ($t eq 'freq_trans') { $items_exists->{$t}{$cpu_num} = -e sprintf("%s/cpu%s/cpufreq/stats/time_in_state", $freq_path, $cpu_num); } - elsif ($t eq 'freq_times') { $items_exists->{$t}{$cpu_num} = -e sprintf("%s/cpu%s/cpufreq/stats/total_trans" , $freq_path, $cpu_num); } - elsif ($t eq 'freq_ttable') { $items_exists->{$t}{$cpu_num} = -e sprintf("%s/cpu%s/cpufreq/stats/trans_table" , $freq_path, $cpu_num); } - } - return $items_exists->{$t}{$cpu_num}; - } - else - { - unless(exists($items_exists->{$t}{'total'})) - { - my $c = 0; - for (my $i=0; $i < $cpuinfo->{'cpu_count'}; $i++) { $c++ if (check_exists($t, $i)); } - $items_exists->{$t}{'total'} = $c > 0; - } - return $items_exists->{$t}{'total'}; - } -} - -# ------------------------- remove unavialable fields from graph -------------------------- -sub remove_unavialabled_counters -{ - my @cpu = split(/\s+/, (grep(/cpu\s/, get_stat_file_content()))[0]); - my $counters_count = scalar(@cpu) - 3; - @cnames = @cnames[0..$counters_count]; -} - -# ----------------------- get sysfs file content ---------------- -my $fcontents = {}; -sub get_sys_file_content -{ - my $file = $_[0]; - return 'nan' if (-z $file); - unless (exists($fcontents->{$file})) - { - open (FH, '<', $file) or die "$! $file \n"; - $fcontents->{$file} = ; - close (FH); - chomp $fcontents->{$file}; - } - return $fcontents->{$file}; -} - -# -------------------------- loading cpu info --------------------------------- -sub load_cpuinfo -{ - my $file = "/proc/cpuinfo"; - open (FH, '<', $file) or die "$! $file \n"; - my $cpu_num = -1; - for my $line () - { - chomp $line; - $cpu_num++ if $line =~ m/^processor\s+:/; - $cpuinfo->{$cpu_num}{'name'} = trim((split(/:/,$line))[1]) if $line =~ m/^model name\s+:/; - $cpuinfo->{$cpu_num}{'bogomips'} = trim((split(/:/,$line))[1]) if $line =~ m/^bogomips\s+:/; - if (not exists($cpuinfo->{$cpu_num}{'info'}) and exists($cpuinfo->{$cpu_num}{'name'}) and exists($cpuinfo->{$cpu_num}{'bogomips'})) - { - $cpuinfo->{$cpu_num}{'info'} = sprintf("[%s (%s bogomips)]", $cpuinfo->{$cpu_num}{'name'}, $cpuinfo->{$cpu_num}{'bogomips'}) ; - } - } - close (FH); - $cpuinfo->{'cpu_count'} = $cpu_num+1; -} - - -# -------------------------- loading stat file lines --------------------------------- -sub get_stat_file_content -{ - if(scalar(@stat_file_content) == 0) - { - open (FH, '<', $stat_file) or die "$! $stat_file \n"; - for () - { - next unless $_ =~ m/cpu/; - chomp $_; - push(@stat_file_content, $_); - } - close (FH); - } - return @stat_file_content; -} - -# -------------------------------- replacing strings ------------------------ -sub replace_template -{ - my ($string, $needle, $replacement) = @_[0..2]; - $string =~ s/$needle/$replacement/g; - return $string; -} - -sub replace_templates -{ - my ($src, $replacement_t, $replacement_i) = @_[0..2]; - my $dst = {}; - for my $key ( keys %{$src} ) - { - $dst->{$key} = $src->{$key}; - if($key =~ m/label|info|title/) { $dst->{$key} = replace_template($dst->{$key}, ':t:', $replacement_t); } - if($key =~ m/info|title/) { $dst->{$key} = replace_template($dst->{$key}, ':i:', $replacement_i); } - } - return $dst; -} - -sub append_order -{ - my ($pg, $graph_name, $field_name) = @_[0..2]; - $pg->{$graph_name}{'graph'}{'order'} = exists($pg->{$graph_name}{'graph'}{'order'}) ? sprintf("%s %s", $pg->{$graph_name}{'graph'}{'order'}, $field_name) : $field_name; -} - -sub append_field -{ - my ($pg, $graph_name, $field_name, $field_src, $replacement_t, $replacement_i) = @_[0..5]; - $pg->{$graph_name}{'fields'}{$field_name} = replace_templates($fields->{$field_src}, $replacement_t, $replacement_i); - append_order($pg, $graph_name, $field_name); -} - -sub append_graph -{ - my ($pg, $graph_name, $category, $graph_src, $replacement_t, $replacement_i) = @_[0..5]; - $pg->{$graph_name}{'graph'} = replace_templates($graphs->{$graph_src}, $replacement_t, $replacement_i); - $pg->{$graph_name}{'graph'}{'category'} = $category; -} - -# ---------------------------------------- preparing data for graphs ------------------------------------ -sub prepare_graphs_fields -{ - my $pg = {}; - # ------------------ cpu_utilisation ----------------------------------- - # ---------- general ---------------------- - append_graph($pg, 'cpu_utilisation', 'cpu', 'cpu_utilisation', '', ''); - for my $cname (@cnames) { append_field($pg, 'cpu_utilisation', $cname, $cname, '', ''); append_utilisation_limits($pg, 'cpu_utilisation', $cname, undef); } - if(check_exists('freq_hz')) { for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++) { append_field($pg, 'cpu_utilisation', sprintf("fp_%s", $i), 'freq_percent', $i, ''); } } - # ---------------- childs ------------------- - if ($cpuinfo->{'cpu_count'} > 1) - { - for (my $i=0; $i < $cpuinfo->{'cpu_count'}; $i++) - { - my $graph_name = sprintf("cpu_utilisation.cpu%s", $i); - append_graph($pg, $graph_name, sprintf("CPU %s", $i), 'cpu_utilisation', $i, $cpuinfo->{$i}{'info'}); - for my $cname (@cnames) { append_field($pg, $graph_name, $cname, $cname, '', ''); append_utilisation_limits($pg, $graph_name, $cname, $i); } - if(check_exists('freq_hz', $i)) { append_field($pg, $graph_name, 'fp', 'freq_percent', '', ''); } - } - } - - - # --------------- cpu_frequency -------------------------------------------- - if(check_exists('freq_trans')) - { - # ------------ general -------------------- - # - cpu frequency transitions - - append_graph($pg, 'cpu_frequency', 'cpu', 'cpu_freq_trans', '', ''); - for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++) { if (check_exists('freq_trans', $i)) { append_field($pg, 'cpu_frequency', sprintf("cpu_%s", $i), 'freq_trans', sprintf("CPU%s", $i), ''); } } - append_field($pg, 'cpu_frequency', 'total', 'freq_trans', 'Total', ''); - # ---------------- childs ------------------- - if(check_exists('freq_times')) - { - my $frequences = get_frequency_times(); - for (my $i=0; $i < $cpuinfo->{'cpu_count'}; $i++) - { - if(check_exists('freq_times', $i)) - { - # - cpu frequencyes - - my $graph_name = sprintf("cpu_frequency.percent_cpu%s", $i); - append_graph($pg, $graph_name, sprintf("CPU %s", $i), 'cpu_freq', $i, $cpuinfo->{$i}{'info'}); - for my $freq (@{$frequences->{'names'}{$i}}) { append_field($pg, $graph_name, sprintf("hz_%s", $freq), 'freq_hz', scaleNumber($freq, 'Hz'), ''); } - # - cpu frequencyes per secund - - $graph_name = sprintf("cpu_frequency.percent_ps_cpu%s", $i); - append_graph($pg, $graph_name, sprintf("CPU %s", $i), 'cpu_freq_ps', $i, $cpuinfo->{$i}{'info'}); - for my $freq (@{$frequences->{'names'}{$i}}) { append_field($pg, $graph_name, sprintf("hz_%s", $freq), 'freq_hz', scaleNumber($freq, 'Hz'), ''); } - } - } - } - if(check_exists('freq_ttable')) - { - my $f_table = get_frequency_trans_table(); - for (my $i=0; $i < $cpuinfo->{'cpu_count'}; $i++) - { - if(check_exists('freq_ttable', $i)) - { - # - cpu frequencyes table - - my $graph_name = sprintf("cpu_frequency.trans_table_cpu%s", $i); - append_graph($pg, $graph_name, sprintf("CPU %s", $i), 'cpu_freq_trans_table', $i, $cpuinfo->{$i}{'info'}); - for my $from (sort keys %{$f_table->{'values'}{$i}}) - { - for my $to (sort keys %{$f_table->{'values'}{$i}{$from}}) - { - next if ($from eq $to); - append_field($pg, $graph_name, sprintf("f_%s_t_%s", $from, $to), 'freq_trans_table', sprintf(". %9s -> %s", scaleNumber($from, 'Hz'), scaleNumber($to, 'Hz')), ''); - } - } - # - cpu frequencyes table per secund - - $graph_name = sprintf("cpu_frequency.trans_table_ps_cpu%s", $i); - append_graph($pg, $graph_name, sprintf("CPU %s", $i), 'cpu_freq_trans_table_ps', $i, $cpuinfo->{$i}{'info'}); - for my $from (sort keys %{$f_table->{'values'}{$i}}) - { - for my $to (sort keys %{$f_table->{'values'}{$i}{$from}}) - { - next if ($from eq $to); - append_field($pg, $graph_name, sprintf("f_%s_t_%s", $from, $to), 'freq_trans_table', sprintf(". %9s -> %s", scaleNumber($from, 'Hz'), scaleNumber($to, 'Hz')), ''); - } - } - } - } - } - } - - - # --------------- cpu_all ----------------------------------------- - if ($cpuinfo->{'cpu_count'} > 1) - { - append_graph($pg, 'cpu_all', 'cpu', 'cpu_all', '', ''); - for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++) { append_field($pg, 'cpu_all', sprintf("cpu_%s", $i), 'cpu_util', sprintf("CPU%s", $i)); } - append_field($pg, 'cpu_all', 'total', 'cpu_util', 'Combined'); - } - return $pg; -} - - -# ------------------------------------ printing limits (for utilisation graphs) ---------------- -sub append_utilisation_limits -{ - my ($pg, $graph_name, $cname, $i) = @_[0..3]; - for my $type (qw(warning critical)) - { - my $field = sprintf("%s_%s", $cname, $type); - my $cpu_field = defined($i) ? sprintf("cpu%s_%s_%s", $i, $cname, $type) : undef; - my $limit = (defined($i) and defined($limits->{'utilisation'}{$cpu_field})) ? - $limits->{'utilisation'}{$cpu_field} : - ( - defined($limits->{'utilisation'}{$field}) ? - $limits->{'utilisation'}{$field} : - ( - defined($limits->{'utilisation'}{$type}) ? - $limits->{'utilisation'}{$type} : - undef - ) - ); - if(defined($limit)) { $pg->{$graph_name}{'fields'}{$cname}{$type} = $limit; } - } -} - -# ---------------- loading limits ------------- -sub load_limits -{ - $limits->{'utilisation'}{'warning'} = $ENV{warning} || undef; - $limits->{'utilisation'}{'critical'} = $ENV{critical} || undef; - for my $cname (@cnames) - { - for my $t (qw(warning critical)) - { - my $name = sprintf("%s_%s", $cname, $t); - $limits->{'utilisation'}{$name} = $ENV{$name} || undef; - for (my $i = 0; $i <= $cpuinfo->{'cpu_count'}; $i++) - { - $name = sprintf("cpu%s_%s_%s",$i, $cname, $t); - $limits->{'utilisation'}{$name} = $ENV{$name} || undef; - } - } - } -} - -# --------------------------------- graph configs ---------------------------- -sub print_config -{ - load_limits(); - my $config = prepare_graphs_fields(); - for my $g (sort keys %{$config}) - { - printf("multigraph %s\n", $g); - for my $go (sort keys %{$config->{$g}{'graph'}}) { printf("graph_%s %s\n", $go, $config->{$g}{'graph'}{$go}); } - for my $f (sort keys %{$config->{$g}{'fields'}}) { for my $fo (sort keys %{$config->{$g}{'fields'}{$f}}) { printf("%s.%s %s\n", $f, $fo, $config->{$g}{'fields'}{$f}{$fo}); } } - print "\n"; - } -} - -# ----------------------------------- saving state data using munin -------------------- -sub save_state_data -{ - my $data = $_[0]; - my $d = Data::Dumper->new([$data]); - $d->Indent(0); - save_state($d->Dump); -} - -# -------------------------------- loading previous state data using munin ------------------- -sub restore_state_data -{ - my $VAR1; - my $states = (restore_state())[0]; - eval $states if defined $states; - return $VAR1; -} - -sub load_stats -{ - my $stats = {}; - # need to store -------------------- - $stats->{'timestamp'} = time(); - $stats->{'cpu_util'} = get_cpu_utilisation_stats() ; - $stats->{'f_trans'} = get_frequency_transitions() if check_exists('freq_trans') ; - $stats->{'f_times'} = get_frequency_times() if check_exists('freq_times') ; - $stats->{'f_ttable'} = get_frequency_trans_table() if check_exists('freq_ttable'); - - save_state_data($stats); - - # no need to store -------------------- - $stats->{'f_minmax'} = get_cpu_curr_max_freqences() if check_exists('freq_hz') ; - - - #print Dumper $stats; - return $stats; -} - -# ---------------------------------- loading cpu stats from loaded lines ------------------------ -sub get_cpu_utilisation_stats -{ - my $stats = {}; - for (my $i = 0; $i <= $cpuinfo->{'cpu_count'}; $i++) - { - my $rx = $i == $cpuinfo->{'cpu_count'} ? 'cpu' : sprintf ("cpu%s", $i); - my $cn = $i == $cpuinfo->{'cpu_count'} ? 'total' : $i; - my @tmp = split(/\s+/, (grep(/$rx\s/, get_stat_file_content()))[0]); - my $j = 1; - for my $cname (@cnames) - { - $stats->{$cn}{$cname} = $tmp[$j]; - $j++; - } - } - return $stats; -} -# ------------------ loading frequency transitions for each cpu ---------------------------- -sub get_frequency_transitions -{ - my $stats = {}; - for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++) - { - next unless (check_exists('freq_trans', $i)); - $stats->{$i} = get_sys_file_content(sprintf("%s/cpu%s/cpufreq/stats/total_trans", $freq_path, $i)); - } - return $stats; -} - -# ------------------ loading frequency times for each cpu ---------------------------- -sub get_frequency_times -{ - my $stat = {}; - for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++) - { - next unless (check_exists('freq_times', $i)); - my $total = 0; - my $file = sprintf("%s/cpu%s/cpufreq/stats/time_in_state", $freq_path, $i); - open (FH, '<', $file) or die "$! $file \n"; - for my $line () - { - chomp $line; - my ($hz, $count) = split(/\s+/, $line); - $hz = $hz*$freq_mul; - $stat->{'values'}{$i}{$hz} = $count; - push(@{$stat->{'names'}{$i}}, $hz); - $total += $count; - } - close (FH); - $stat->{'total'}{$i} = $total; - } - return $stat; -} - -# ------------------ loading current and max frequency for each cpu ---------------------------- -sub get_cpu_curr_max_freqences -{ - my $freq = {}; - for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++) - { - next unless (check_exists('freq_hz', $i)); - my $cpu_path = sprintf("%s/cpu%s/cpufreq", $freq_path, $i); - $freq->{'cur'}{$i} = get_sys_file_content(sprintf("%s/scaling_cur_freq", $cpu_path))*$freq_mul; - $freq->{'max'}{$i} = get_sys_file_content(sprintf("%s/scaling_max_freq", $cpu_path))*$freq_mul; - $freq->{'min'}{$i} = get_sys_file_content(sprintf("%s/scaling_min_freq", $cpu_path))*$freq_mul; - } - return $freq; -} - -sub get_frequency_trans_table -{ - my $tbl = {}; - for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++) - { - next unless (check_exists('freq_ttable', $i)); - my @frequences; - my $fcount = 0; - my $total = 0; - my $file = sprintf("%s/cpu%s/cpufreq/stats/trans_table", $freq_path, $i); - open (FH, '<', $file) or die "$! $file \n"; - for my $line () - { - chomp $line; - my ($left, $right) = split(/:/, $line); - next if($left =~ m/From/); - if($left =~ m/\d+/) - { - my $frequence = trim($left)*$freq_mul; - my @counters = split(/\s+/, trim($right)); - for (my $j = 0; $j<$fcount; $j++) - { - $tbl->{'values'}{$i}{$frequence}{$frequences[$j]*$freq_mul} = $counters[$j]; - $total += $counters[$j]; - } - } - else - { - @frequences = split(/\s+/, trim($right)); - $fcount = scalar(@frequences); - } - } - $tbl->{'total'}{$i} = $total; - close (FH); - } - return $tbl; -} - -sub one_second_part -{ - my ($curr, $prev, $timediff) = @_[0..2]; - #print "$prev, $curr, $timediff\n"; - return 'NaN' if ($curr < $prev or $timediff < 0); - return $curr - $prev if $timediff == 0; - return ($curr - $prev)/$timediff; -} - -sub divide -{ - my ($divider, $divident) = @_[0..1]; - return 'NaN' if $divident == 0; - return $divider/$divident; -} - -# -------------------------------- calculating fields values ------------------------------ -sub calculate -{ - my ($pstats, $cstats) = @_[0..1]; - my $result = {}; - my $timediff = $cstats->{'timestamp'} - $pstats->{'timestamp'}; - # --- cpu utilisation ---- - for my $cpu (keys %{$cstats->{'cpu_util'}}) - { - # ------ calculating 1% - $result->{'cpu_util'}{'1%'}{$cpu} = 0; - for my $cname (@cnames) - { - $result->{'cpu_util'}{'diff'}{$cpu}{$cname} = one_second_part($cstats->{'cpu_util'}{$cpu}{$cname}, $pstats->{'cpu_util'}{$cpu}{$cname}, $timediff); - $result->{'cpu_util'}{'1%'}{$cpu} += $result->{'cpu_util'}{'diff'}{$cpu}{$cname} if $result->{'cpu_util'}{'diff'}{$cpu}{$cname} ne 'NaN'; - } - $result->{'cpu_util'}{'1%'}{$cpu} = $result->{'cpu_util'}{'1%'}{$cpu}/100; - # ------ calculating used percents - $result->{'cpu_util'}{'used'}{$cpu} = 0; - for my $cname (@cnames) - { - $result->{'cpu_util'}{'%'}{$cpu}{$cname} = divide($result->{'cpu_util'}{'diff'}{$cpu}{$cname}, $result->{'cpu_util'}{'1%'}{$cpu}); - next if $cname eq 'idle'; - $result->{'cpu_util'}{'used'}{$cpu} += $result->{'cpu_util'}{'%'}{$cpu}{$cname} if $result->{'cpu_util'}{'%'}{$cpu}{$cname} ne 'NaN'; - } - } - # ------ freq min max ---- - if (check_exists('freq_hz')) - { - for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++) - { - $result->{'f_minmax'}{'%'}{$i} = divide($cstats->{'f_minmax'}{'cur'}{$i} - $cstats->{'f_minmax'}{'min'}{$i}, - (($cstats->{'f_minmax'}{'max'}{$i} - $cstats->{'f_minmax'}{'min'}{$i}) / 100 )) if (check_exists('freq_hz', $i)); - } - } - # ---- freq trans ---- - if (check_exists('freq_trans')) - { - $result->{'f_trans'}{'total'} = 0; - for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++) - { - if(check_exists('freq_trans', $i)) - { - $result->{'f_trans'}{$i} = one_second_part($cstats->{'f_trans'}{$i}, $pstats->{'f_trans'}{$i}, $timediff); - $result->{'f_trans'}{'total'} += $result->{'f_trans'}{$i} if $result->{'f_trans'}{$i} ne 'NaN'; - } - } - } - # -- freq times --- - if (check_exists('freq_times')) - { - for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++) - { - if (check_exists('freq_times', $i)) - { - my $oneprc = $cstats->{'f_times'}{'total'}{$i}/100; - my $ps_total = 0; - for my $hz (@{$cstats->{'f_times'}{'names'}{$i}}) - { - $result->{'f_times'}{$i}{$hz} = divide($cstats->{'f_times'}{'values'}{$i}{$hz}, $oneprc); - $cstats->{'f_times'}{'%_ps'}{$i}{$hz} = one_second_part($cstats->{'f_times'}{'values'}{$i}{$hz}, $pstats->{'f_times'}{'values'}{$i}{$hz}, $timediff); - $ps_total += $cstats->{'f_times'}{'%_ps'}{$i}{$hz}; - } - $ps_total = $ps_total/100; - for my $hz (@{$cstats->{'f_times'}{'names'}{$i}}) - { - $result->{'f_times_ps'}{$i}{$hz} = divide($cstats->{'f_times'}{'%_ps'}{$i}{$hz}, $ps_total); - } - } - } - } - # ------- freq trans table --- - if (check_exists('freq_ttable')) - { - for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++) - { - if (check_exists('freq_ttable', $i)) - { - my $oneprc = $cstats->{'f_ttable'}{'total'}{$i}/100; - my $ps_total = 0; - for my $from (keys %{$cstats->{'f_ttable'}{'values'}{$i}}) - { - for my $to (keys %{$cstats->{'f_ttable'}{'values'}{$i}{$from}}) - { - next if ($from eq $to); - $result->{'f_ttable'}{$i}{$from}{$to} = divide($cstats->{'f_ttable'}{'values'}{$i}{$from}{$to}, $oneprc); - $cstats->{'f_ttable'}{'%_ps'}{$i}{$from}{$to} = one_second_part($cstats->{'f_ttable'}{'values'}{$i}{$from}{$to}, $pstats->{'f_ttable'}{'values'}{$i}{$from}{$to}, $timediff); - $ps_total += $cstats->{'f_ttable'}{'%_ps'}{$i}{$from}{$to}; - } - } - $ps_total = $ps_total/100; - for my $from (keys %{$cstats->{'f_ttable'}{'values'}{$i}}) - { - for my $to (keys %{$cstats->{'f_ttable'}{'values'}{$i}{$from}}) - { - next if ($from eq $to); - $result->{'f_ttable_ps'}{$i}{$from}{$to} = divide($cstats->{'f_ttable'}{'%_ps'}{$i}{$from}{$to}, $ps_total); - } - } - } - } - } - #print Dumper $result; - return $result; -} - -# ---------------------------------------- preparing values ------------------------------------ -sub prepare_graphs_values -{ - my ($data) = $_[0]; - my $pg = {}; - # ------------------ cpu_utilisation ----------------------------------- - # ---------- general ---------------------- - for my $cname (@cnames) { $pg->{'cpu_utilisation'}{$cname} = $data->{'cpu_util'}{'%'}{'total'}{$cname}; } - if(check_exists('freq_hz')) { for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++) { $pg->{'cpu_utilisation'}{sprintf("fp_%s", $i)} = $data->{'f_minmax'}{'%'}{$i}; } } - # ---------------- childs ------------------- - if ($cpuinfo->{'cpu_count'} > 1) - { - for (my $i=0; $i < $cpuinfo->{'cpu_count'}; $i++) - { - my $graph_name = sprintf("cpu_utilisation.cpu%s", $i); - for my $cname (@cnames) { $pg->{$graph_name}{$cname} = $data->{'cpu_util'}{'%'}{$i}{$cname}; } - if(check_exists('freq_hz', $i)) { $pg->{$graph_name}{'fp'} = $data->{'f_minmax'}{'%'}{$i}; } - } - } - - - # --------------- cpu_frequency -------------------------------------------- - if(check_exists('freq_trans')) - { - # ------------ general -------------------- - for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++) { $pg->{'cpu_frequency'}{sprintf("cpu_%s", $i)} = $data->{'f_trans'}{$i} if (check_exists('freq_trans', $i)); } - $pg->{'cpu_frequency'}{'total'} = $data->{'f_trans'}{'total'}; - # ---------------- childs ------------------- - if(check_exists('freq_times')) - { - for (my $i=0; $i < $cpuinfo->{'cpu_count'}; $i++) - { - if(check_exists('freq_times', $i)) - { - my $graph_name = sprintf("cpu_frequency.percent_cpu%s", $i); - for my $freq (keys %{$data->{'f_times'}{$i}}) { $pg->{$graph_name}{sprintf("hz_%s", $freq)} = $data->{'f_times'}{$i}{$freq}; } - $graph_name = sprintf("cpu_frequency.percent_ps_cpu%s", $i); - for my $freq (keys %{$data->{'f_times_ps'}{$i}}) { $pg->{$graph_name}{sprintf("hz_%s", $freq)} = $data->{'f_times_ps'}{$i}{$freq}; } - } - } - } - if(check_exists('freq_ttable')) - { - for (my $i=0; $i < $cpuinfo->{'cpu_count'}; $i++) - { - if(check_exists('freq_ttable', $i)) - { - my $graph_name = sprintf("cpu_frequency.trans_table_cpu%s", $i); - for my $from (keys %{$data->{'f_ttable'}{$i}}) - { - for my $to (keys %{$data->{'f_ttable'}{$i}{$from}}) - { - $pg->{$graph_name}{sprintf("f_%s_t_%s", $from, $to)} = $data->{'f_ttable'}{$i}{$from}{$to}; - } - } - $graph_name = sprintf("cpu_frequency.trans_table_ps_cpu%s", $i); - for my $from (keys %{$data->{'f_ttable_ps'}{$i}}) - { - for my $to (keys %{$data->{'f_ttable_ps'}{$i}{$from}}) - { - $pg->{$graph_name}{sprintf("f_%s_t_%s", $from, $to)} = $data->{'f_ttable_ps'}{$i}{$from}{$to}; - } - } - } - } - } - } - - # --------------- cpu_all -------------------------------------------- - if ($cpuinfo->{'cpu_count'} > 1) - { - for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++) { $pg->{'cpu_all'}{sprintf("cpu_%s", $i)} = $data->{'cpu_util'}{'used'}{$i}; } - $pg->{'cpu_all'}{'total'} = $data->{'cpu_util'}{'used'}{'total'}; - } - return $pg; -} - - - -# -------------------------------- printing values ----------------------------------- -sub print_values -{ - my $pstats = restore_state_data(); - my $cstats = load_stats(); - if (exists ($pstats->{'timestamp'})) - { - my $values = prepare_graphs_values(calculate($pstats, $cstats)); - #print Dumper $values; - for my $g (sort keys %{$values}) - { - printf("multigraph %s\n", $g); - for my $f (sort keys %{$values->{$g}}) { printf("%s.value %s\n", $f, $values->{$g}{$f}); } - print "\n"; - } - } -} - -__END__ - -- user: normal processes executing in user mode -- nice: niced processes executing in user mode -- system: processes executing in kernel mode -- idle: twiddling thumbs -- iowait: waiting for I/O to complete -- irq: servicing interrupts -- softirq: servicing softirqs -- steal: involuntary wait -- guest: running a guest diff --git a/plugins/system/cpu_by_process b/plugins/system/cpu_by_process deleted file mode 100755 index 41d33d53..00000000 --- a/plugins/system/cpu_by_process +++ /dev/null @@ -1,135 +0,0 @@ -#!/bin/sh -# -# Copyright (C) 2006 Holger Levsen -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 dated June, -# 1991. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# Configuration variables -# vservers - specify the vservers to include in the graph (default: all) -# limits - if true, turn on limit graphing (default: false) -# -# NOTE: If no configuration variables are set, the defaults will be used - -# Example /etc/munin/plugin-conf.d/munin-node -# -# The first group monitors the vservers named "vserver1 vserver2 -# vserver3 vserver4" and looks to see if the resource limit has been -# breached, if so it sends a message to nagios via send_nsca, and -# sends an email to notify that this has happened. -# -# The second monitors the vservers "vserver5 vserver6 vserver7" and -# has no limit notifications turned on. -# -# The third monitors all vservers on the system, in one graph, and it has -# no limit notifications defined. -# -# You can use any combination of these to fit your needs. -# -# -# [vsrmem_group1] -# user root -# env.vservers vserver1 vserver2 vserver3 vserver4 -# env.limits 1 -# contacts nagios email -# contact.nagios.command /usr/bin/send_nsca -H your.nagios-host.here -c /etc/send_nsca.cfg -# contact.email.command mail -s "Munin-notification for ${var:group} :: ${var:host}" your@email.address.here -# -# [vsrmem_group2] -# user root -# env.vservers vserver5 vserver6 vserver7 -# env.limits 0 -# -# [vserver_rmemory] -# user root -# -# Graph Vserver RSS usage and limits -# -# Changelog -# version 0.1 - 2006 April xx - Holger Levsen -# - initial author -# version 0.2 - 2006 April 24 - Micah Anderson -# - Add dynamic arch page size determination -# - Some cleanup and clarification -# version 0.3 - 2006 May 3 - Micah Anderson -# - Add ability to group vservers via environment vars -# - Fix missing close quotes and standardize indents -# - Add limit notification -# - Update documentation to include info on groups and limits -# version 0.4 - 2006 Jun 22 - Micah Anderson -# - Fix error that results if NodeName is set to include a domain name - -#scriptname=`basename $0` -#vsname=`echo $scriptname | perl -ne '/^vserver_proc_VM_(.*)/ and print $1'` - -#if [ "$1" = "suggest" ]; then -# ls -1 /etc/vservers -# exit 0 -#elif [ -z "$vsname" ]; then -# echo "Must be used with a vserver name; try '$0 suggest'" >&2 -# exit 2 -#fi - -#xid=`cat /etc/vservers/$vsname/context` - -if [ "$1" = "config" ]; then - echo "graph_title CPU time by Process" - echo 'graph_args --base 1000 -l 0' - echo 'graph_vlabel seconds' - echo 'graph_category system' - echo "graph_info Shows CPU time used by each process name" - - # ps -eo time,comm h | perl -e ' - ps -eo pid,time,comm | perl -e ' - $junk = <>; - while (<>) - { - @a = split; - $proc = $a[2]; - $var = $proc; - $var =~ s|/.*||; - $var =~ s|\.$||; - $var =~ tr|a-zA-Z0-9|_|c; - $procs{$var} = $proc; - } - my $stack = 0; - sub draw() { return $stack++ ? "STACK" : "AREA" } - print map - { - "$_.label $procs{$_}\n" . - "$_.min 0\n" . - "$_.type DERIVE\n" . - "$_.draw " . draw() . "\n" - } - sort keys %procs; - ' - exit 0 -else - # ps -eo time,comm h | perl -e ' - ps -eo pid,time,comm | perl -e ' - $junk = <>; - while (<>) - { - @a = split; - $cpu = $a[1]; - $var = $a[2]; - $var =~ s|/.*||; - $var =~ s|\.$||; - $var =~ tr|a-zA-Z0-9|_|c; - @b = split /:/, $cpu; - $cpu = (($b[0] * 60) + $b[1]) * 60 + $b[2]; - $total{$var} += $cpu; - } - print map {"$_.value $total{$_}\n"} sort keys %total' -fi diff --git a/plugins/system/cpufreq b/plugins/system/cpufreq deleted file mode 100755 index 3f25b7c5..00000000 --- a/plugins/system/cpufreq +++ /dev/null @@ -1,55 +0,0 @@ -#!/bin/sh -# -# Plugin to monitor CPU speed on system that allow to reduce it for power saving -# -# probably only useful on laptops if you configure it to lower CPU frequency for -# less power consumptoin. This plugin works by reading from the /sys file system. -# -# by dominik dot stadler at gmx.at -# -# Magic markers (optional - only used by munin-config and some -# installation scripts): -# -#%# family=auto -#%# capabilities=autoconf - -if [ "$1" = "autoconf" ]; then - if [ -r /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq ]; then - echo yes - exit 0 - else - echo no - exit 1 - fi -fi - - -if [ "$1" = "config" ]; then - echo 'graph_title CPU speed' - echo 'graph_args --base 1000 -l 0' - echo 'graph_vlabel speed' -# echo 'graph_scale no' - echo 'graph_category system' - echo 'graph_info This graph shows the speed of the system fan.' - echo 'maxspeed.label maxspeed' - echo 'maxspeed.info The maximum speed of the CPU.' - echo 'minspeed.label minspeed' - echo 'minspeed.info The minimum speed of the CPU.' - echo 'cpuspeed.label speed' - echo 'cpuspeed.info The current speed of the CPU.' - - exit 0 -fi - -echo -n "maxspeed.value " -cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq -echo -n "minspeed.value " -cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq -echo -n "cpuspeed.value " - -# cpuinfo_cur_freq is not readable for user munin, therefore using scaling_cur_freq for now, should -# be equal information, see http://wiki.ubuntuusers.de/Prozessortaktung -# We could also configure this plugin to require root access, but I like it better this way. -#cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq -cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq - diff --git a/plugins/system/cpufreq-avg b/plugins/system/cpufreq-avg deleted file mode 100755 index 0544722f..00000000 --- a/plugins/system/cpufreq-avg +++ /dev/null @@ -1,144 +0,0 @@ -#!/bin/sh -# -# Plugin to measure average CPU frequency for each CPU/core. -# -# Contributed by Mark Edwards -# -# Usage: Place in /etc/munin/plugins (or link it there using ln -s) -# -# Parameters understood: -# -# config (required) -# autoconf (optional - used by munin-config) -# -# $Log$ -# -# Revision 0.2 2010/01/04 16:37:00 medwards -# Minor bugfixes in config section -# -# Revision 0.1 2010/01/04 16:13:00 medwards -# First version -# -# -# -# Magic markers - optional - used by installation scripts and -# munin-config: -# -#%# family=auto -#%# capabilities=autoconf - -if [ "$1" = "autoconf" ]; then - if [ -r /sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state ]; then - echo yes - exit 0 - else - echo no - exit 1 - fi -fi - -cpu_count=`grep -c "^processor" /proc/cpuinfo` - -if [ "$1" = "config" ]; then - - echo 'graph_title Average CPU Frequency' - up_lim=`cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq` - low_lim=`cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq` - up_lim=`expr $up_lim \* 1000` # Convert to Hz - low_lim=`expr $low_lim \* 1000` # Convert to Hz - echo "graph_args -u $up_lim -l $low_lim -r --base 1000" - echo 'graph_vlabel Hz' - echo 'graph_category system' - cpu=0 - while [ $cpu -lt $cpu_count ] - do - echo "cpu_$cpu.label CPU $cpu" - echo "cpu_$cpu.type GAUGE" - echo "cpu_$cpu.info Hz" - cpu=`expr $cpu + 1` - done - exit 0 -fi - -# Run measurements -cpu=0 -while [ $cpu -lt $cpu_count ] -do - - time_in_state=`cat /sys/devices/system/cpu/cpu$cpu/cpufreq/stats/time_in_state` - - # Check/create statefile(s) - statefile="/var/lib/munin/plugin-state/cpufreq-avg_cpu$cpu.state" - if [ ! -r "$statefile" ] - then - echo "$time_in_state" > $statefile - if [ "$?" -ne "0" ] - then - exit ${1} - else - cpu=`expr $cpu + 1` - continue - fi - fi - state=`cat $statefile` - - # Calculated total time since last state - total_time=0 - total_time=$( - echo "$time_in_state" | { - i=0 - while read line - do - this_freq=`echo $line | awk '{ print $1; }'` - this_time=`echo $line | awk '{ print $2; }'` - this_time_state=`echo "$state" | grep $this_freq | awk '{ print $2; }'` - if [ $this_time -ge $this_time_state ] # Only measure if state is valid - then - time_diff=`expr $this_time - $this_time_state` # Calculate time since last state - total_time=`expr $total_time + $time_diff` - fi - i=`expr $i + 1` - done - echo $total_time - } - ) - - # Measure average CPU frequency if total time calculation was successful - - frequency=0 - frequency=$( - echo "$time_in_state" | { - i=0 - while read line - do - this_freq=`echo $line | awk '{ print $1; }'` - this_time=`echo $line | awk '{ print $2; }'` - this_time_state=`echo "$state" | grep $this_freq | awk '{ print $2; }'` - this_freq=`expr $this_freq \* 1000` # Convert to Hz - this_time=`expr $this_time - $this_time_state` # Calculate time since last state - if [ $total_time -gt 0 ] - then - calc=`echo "($this_time / $total_time) * $this_freq" | bc -l` - frequency=`echo "$frequency + $calc" | bc -l` - fi - i=`expr $i + 1` - done - echo $frequency - } - ) - - # Round result to an integer and return it - frequency=`echo "scale=0 ; ($frequency+0.5)/1" | bc -l` - if [ $frequency -gt 0 ] - then - echo "cpu_$cpu.value $frequency" - fi - - # Update statefile - echo "$time_in_state" > $statefile - - cpu=`expr $cpu + 1` - -done - -exit 0 \ No newline at end of file diff --git a/plugins/system/cpufreq-info b/plugins/system/cpufreq-info deleted file mode 100755 index 103827a7..00000000 --- a/plugins/system/cpufreq-info +++ /dev/null @@ -1,100 +0,0 @@ -#!/bin/bash -# -# Plugin to measure CPU frequency via cpufreq-info binary. -# This makes the plugin run on linux machines only. -# However the same goes for using sysfs directly. -# -# Contributed by Jo Schulze -# -# Config variables: -# -# -# Requires: -# cpufrequtils http://www.kernel.org/pub/linux/utils/kernel/cpufreq/cpufrequtils.html -# -# @remarks -# jo20061130 using cpufreq-info should simplify the whole thing -# jo20061202 tested on AMD K8 X2, intel Core 2 Duo -# -# $Log$ -# -# Magic markers - optional - used by installation scripts and -# munin-config: -# -#%# family=manual -#%# capabilities=autoconf - -LC_ALL="C" - -CINFOBIN="/usr/bin/cpufreq-info" - -nCPU=$(grep -c "^processor" /proc/cpuinfo) - -function getFreq () -{ - i=0 - while ((i < nCPU)); do - affc=`$CINFOBIN -a -c $i` - internal=`echo $affc | tr ' ' '_'` - cpus=( $affc ) - n=${#cpus[@]} - - freq=`$CINFOBIN -f -c $i` - echo "freq_$internal.value $freq" - - ((i += n)) - done -} - -function getAvail () -{ - i=0 - while ((i < nCPU)); do - affc=`$CINFOBIN -a -c $i` - internal=`echo $affc | tr ' ' '_'` - label=`echo $affc | tr ' ' ','` - cpus=( $affc ) - n=${#cpus[@]} - - echo "freq_$internal.label CPU $i (Core $label)" - echo "freq_$internal.type GAUGE" - echo "freq_$internal.info Hz" - - ((i += n)) - done -} - -function config () -{ -cat <) - { - $d =~ /(cpu[0-9]+)/i; - print "$1\n"; - } - exit 0; -} - -if (eval "require Storable") -{ - Storable->import(qw /lock_store lock_nstore lock_retrieve/); -} -else -{ - die "Sorry, you don't have Storable. (update your perl!)\n"; -} - -# Let's see what is requested. -my $target; -if ($0 =~ /_(cpu\d+)$/i) -{ - $target = $1; -} -else -{ - die "Error: we need to know what cpu you want, so link this plugin as cpufreq_cpu0 orso."; -} - -my $statedir = $ENV{"statedir"} || "/var/lib/munin/plugin-state/"; -my $statefile = $statedir.($ENV{"statefile"} || "/cpufreq_$target.state"); -$statefile = glob($statefile); # Make it saner, remove tildes. Not foolproof though ;) - -my $cpufreq; - -eval { $cpufreq = lock_retrieve($statefile); }; -unless(defined($cpufreq)) -{ - print STDERR "Couldn't read state file! (ignore this error on first run)\n"; - $cpufreq = {}; -} - -my $cpufreq_now = {}; -foreach my $d () -{ - unless(open(TIS, "<", "$d"."/time_in_state")) - { - die "Could not open ${d}/time_in_state: $!\n"; - } - $d =~ /(cpu[0-9]+)/i; - my $cpu = $1; - while() - { - if (/^(\d+)\s(\d+)/) - { - $cpufreq_now->{$cpu}{$1} = $2; - $cpufreq_now->{total}{$cpu} = 0 unless(defined($cpufreq_now->{$cpu})); - $cpufreq_now->{total}{$cpu} += $2; - } - } - close(TIS); -} - -# Let's figure out the percentages. -my %freq_p; -my $cpu = $target; -foreach my $freq (keys %{$cpufreq_now->{$cpu}}) -{ - my $new = $cpufreq_now->{$cpu}{$freq}; - my $old = (defined($cpufreq->{$cpu}{$freq})?$cpufreq->{$cpu}{$freq}:0); # If no old data, we average everything. - my $total = $cpufreq_now->{total}{$cpu}; - $total -= $cpufreq->{total}{$cpu} if (defined($cpufreq->{total}{$cpu})); - if (defined($total) && $total > 0) - { - my $p = ($new - $old) / $total; - $freq_p{$cpu}{$freq} = $p * 100.0; - $freq_p{avg}{$cpu} += $p * $freq; # Average speed, weighted average - } - else - { - $freq_p{$cpu}{$freq} = 0; - } -} - -if ( $ARGV[0] and $ARGV[0] eq "config" ) -{ - print "graph_title Cpu frequency usage of $target\n"; - print "graph_args --base 1000 -l 0\n"; - print "graph_vlabel CPU Frequency %\n"; - print "graph_category System\n"; - print "graph_info This graph shows information about the cpu frequency scaling of your CPU(s)\n"; - - my $count = 0; - my ($r,$g,$blue) = (0,255,0); - my $range = scalar(keys(%{$freq_p{$cpu}})); - my $step = (255+255) / ($range - 1); - # In order to let color work, let's go from green to yellow to red -- 00FF00 to FFFF00 to FF0000. 256 and 256 steps. - foreach my $freq (sort { $a <=> $b } keys %{$freq_p{$cpu}}) - { - printf "freq_%d.label %s\n", $freq, "$freq KHz"; - printf "freq_%d.info %s\n", $freq, "Time $cpu spent (percentage) running on $freq KHz"; - printf "freq_%d.type GAUGE\n", $freq; - printf "freq_%d.draw %s\n", $freq, ($count++?"STACK":"AREA"); - printf "freq_%d.colour %02X%02X%02X\n", $freq, $r,$g,$blue; - # Update color - my $s = $step; - if ($r < 255) - { - $r += $s; - if ($r > 255) - { - $s = $r - 255; - $r = 255; - $g -= $s; - } - } - else - { - $g -= $step; - $g = 0 if ($g < 0); - } - } - printf "cpuspeed_avg.label %s\n", "Average speed of cpu $cpu"; - printf "cpuspeed_avg.info %s\n", "Average speed of cpu $cpu scaled to a percentage"; - printf "cpuspeed_avg.GAUGE\n"; - printf "cpuspeed_avg.LINE2\n"; - printf "cpuspeed_avg.colour 0000FF\n"; - exit 0; -} - -# Print requested garbage. -foreach my $freq (sort { $a <=> $b } keys %{$freq_p{$cpu}}) -{ - printf "freq_%d.value %s\n", $freq, $freq_p{$cpu}{$freq}; -} -# Average speed should be as a percentage as well. So divide it by the max freq. -my $max_freq = (sort { $a <=> $b } keys %{$freq_p{$cpu}})[-1]; -if (defined($max_freq) && $max_freq > 0) -{ - printf "cpuspeed_avg.value %d\n", $freq_p{avg}{$cpu} / $max_freq * 100; -} - -# Save state! -eval { lock_nstore($cpufreq_now, $statefile) }; -if ($@) -{ - print STDERR "[$0] Error writing state file!\n"; -} - -exit 0; diff --git a/plugins/system/cpuspeed b/plugins/system/cpuspeed deleted file mode 100755 index 98766372..00000000 --- a/plugins/system/cpuspeed +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash -# -# Munin-plugin to monitor the CPU temperature using mbmon -# -# Used the plugin-frame from "cpuload" by Bjørn Ruberg, published under the GNU GPL -# -# Plugin monitors the cpu-frequency for both cores of my AMD 64-X2-3600+. Works fine running Debian -# GNU/Linux 4.0 (Etch). I do not know any perl and will not proceed in developing this plugin any further. -# If you wish to, feel free to do so yourself. If I am able to, I will help with any problems, contact me -# via Mail (x-stars gmx.de) or through the Debian-German-User-Mailing-List. -# -# Frank-Michael Schulze, 21-09-2007 -# Licensed under: GNU GPL - - - - -if [ "$1" = "config" ]; then - echo "graph_title CPU speed" - echo 'graph_category system' - echo "graph_info This graph shows the cpu-speed for each core, as reported by the kernel" - echo 'core0.label Core 0 speed in MHz' - echo 'core1.label Core 1 speed in MHz' - echo "core0.info Core 0 speed in MHz" - echo "core1.info Core 1 speed in MHz" - echo "core0.type GAUGE" - echo "core1.type GAUGE" - exit 0 -fi - -echo -n "core0.value " -cat /proc/cpuinfo | grep MHz | head -n 1 | cut -c 12- | awk '{ sum += $1 } END { print sum }' -echo -n "core1.value " -cat /proc/cpuinfo | grep MHz | tail -n 1 | cut -c 12- | awk '{ sum += $1 } END { print sum }' diff --git a/plugins/system/cpuspeed2 b/plugins/system/cpuspeed2 deleted file mode 100755 index 4a5801c9..00000000 --- a/plugins/system/cpuspeed2 +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash -# -# Munin-plugin to monitor the cpu speeds of all available cpus -# -# Armin Haaf, 4-11-2007 -# Licensed under: GNU GPL - -MAX_CORES=1024 - - -if [ "$1" = "config" ]; then - echo "graph_title CPU speed" - echo 'graph_category system' - echo "graph_info This graph shows the cpu-speed for each core, as reported by the kernel" - - i=0 - while [ $i -lt $MAX_CORES ] - do - MODEL=`cat /proc/cpuinfo | grep -A 6 "processor.*:.*$i" | grep "model name"` - if [ $? -ne 0 ] - then - break - fi - MODEL=`echo $MODEL | cut -c 12-` - echo "core$i.label Core $i speed in MHz" - echo "core$i.info Core $i speed in MHz $MODEL" - echo "core$i.type GAUGE" - i=$[$i+1] - done - exit 0 -fi - -i=0 -while true -do - cat /proc/cpuinfo | grep -A 6 "processor.*:.*$i" > /dev/null - if [ $? -ne 0 ] - then - break - fi - echo -n "core$i.value " - cat /proc/cpuinfo | grep -A 6 "processor.*:.*$i" | grep "cpu MHz" | cut -c 12- | cut -f 1 -d . - i=$[$i+1] -done - diff --git a/plugins/system/cpuspeed_sane b/plugins/system/cpuspeed_sane deleted file mode 100755 index 097b9f63..00000000 --- a/plugins/system/cpuspeed_sane +++ /dev/null @@ -1,60 +0,0 @@ -#!/bin/sh -# -# Plugin to graph cpu speed on FreeBSD -# -# Parameters: -# -# sysctl - Override path to sysctl program -# -#%# family=auto -#%# capabilities=autoconf - -sysctl=${sysctl:-/sbin/sysctl} - -if [ "$1" = "autoconf" ]; then - if [ -x ${sysctl} ]; then - ${sysctl} dev.cpu.0.freq 2>/dev/null | grep 'dev' >/dev/null 2>/dev/null - if [ "$?" = "0" ]; then - echo yes - exit 0 - fi - echo "no (dev.cpu.0.freq not found)" - exit 1 - else - echo "no (sysctl binary not found)" - exit 2 - fi -fi - -if [ "$1" = "config" ]; then - - echo 'graph_title CPU speed' - echo 'graph_args --base 1000 -l 0' - echo 'graph_vlabel speed in MHz' - echo 'graph_category system' - echo 'graph_scale no' - echo 'graph_info Current CPU speed in MHz. Available levels for the CPU:' `$sysctl -n dev.cpu.0.freq_levels|sed 's!/[0-9]*!!g;s! !, !g'` 'MHz' - - echo cpu0.label cpu0 - echo cpu0.info `$sysctl -n hw.model` Speed - - exit 0 -fi - -file=/usr/local/var/munin/plugin-state/cpuspeed - -echo -n "cpu0.value " -if find $file -mtime -300s 2>/dev/null|grep -Fq $file ; then - head -1 $file -else - $sysctl -n dev.cpu.0.freq -fi - -# Get/cache cpuspeed "later". -export sysctl file -sh -c '( - rand=$(dd if=/dev/urandom bs=1 count=1 2>/dev/null|od -A n -D) - rand=$(expr $rand \* 60 / 256 + 25) - sleep $rand - $sysctl -n dev.cpu.0.freq > $file -)&' >/dev/null 2>&1 diff --git a/plugins/system/uptime b/plugins/system/uptime deleted file mode 100755 index f12b4be7..00000000 --- a/plugins/system/uptime +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/sh -# -# Plugin to get system uptime and kernel age -# -# Magic markers - optional - used by installation scripts and -# munin-config: -# -#%# family=manual -#%# capabilities=autoconf - -if [ "$1" = "autoconf" ]; then - echo "yes" - exit 0 -fi - -if [ "$1" = "config" ]; then - - cat </dev/null 2>&1"); - my $ret = system(@who); - if ($ret == 0) { - print "yes\n"; - exit 0; - } else { - print "no\n"; - exit 1; - } -} - -if ((exists $ARGV[0]) && ($ARGV[0] eq "config")) { - print "graph_title Users Online\n"; - print "graph_args --base 1000 -l 0\n"; - print "graph_scale no\n"; - print "graph_vlabel Number of users\n"; - print "graph_category system\n"; - print "graph_info This graph shows the amount of (unique) users logged in\n"; - print "users.label total users\n"; - print "users.info something like who | wc -l\n"; - print "uusers.label unique users\n"; - print "uusers.info something like who | cut -f -1 -d ' ' | sort | uniq | wc -l\n"; - exit 0; -} - -my @who = split(/\s+/, `$who_cmd -q | head -1`); -print "users.value ".scalar(@who)."\n"; - -my %who; -$who{$_} = 1 foreach (@who); -print "uusers.value ".scalar(keys %who)."\n"; - From 6b25a2f4113a10f82a7ea7a8093b48801c68a123 Mon Sep 17 00:00:00 2001 From: Kenyon Ralph Date: Mon, 27 Feb 2012 23:56:56 -0800 Subject: [PATCH 106/123] add file_age plugin by Edward Plainview https://it.sverigedemokraterna.se/program/file_age/ --- plugins/disk/file_age | 133 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100755 plugins/disk/file_age diff --git a/plugins/disk/file_age b/plugins/disk/file_age new file mode 100755 index 00000000..29636d14 --- /dev/null +++ b/plugins/disk/file_age @@ -0,0 +1,133 @@ +#!/bin/bash + +. $MUNIN_LIBDIR/plugins/plugin.sh + +case $1 in + config) + GRAPH_ORDER="" + COUNTER=1 + while [ $COUNTER -gt 0 ]; do + FILE_PATH="file${COUNTER}_path" + + # Is the path for this file specified? + eval FILE=\$$FILE_PATH + if [ "$FILE" == "" ]; then + break; + fi + + # It is! Add it to the graphs. + GRAPH_ORDER="$GRAPH_ORDER file_$COUNTER" + + # Does this file have a specified label? + LABEL_COUNTER="file${COUNTER}_label" + eval LABEL=\$$LABEL_COUNTER + if [ "$LABEL" == "" ]; then + LABEL=`basename $FILE` + fi + + # Associated warning level? + WARNING="file${COUNTER}_warning" + eval WARNING=\$$WARNING + if [ "$WARNING" != "" ]; then + echo "file_$COUNTER.warning $WARNING" + fi + + # Associated critical level? + CRITICAL="file${COUNTER}_critical" + eval CRITICAL=\$$CRITICAL + if [ "$CRITICAL" != "" ]; then + echo "file_$COUNTER.critical $CRITICAL" + fi + + echo "file_$COUNTER.label $LABEL" + echo "file_$COUNTER.type GAUGE" + echo "file_$COUNTER.min 0" + let COUNTER=COUNTER+1 + done; + + echo "graph_order $GRAPH_ORDER" + echo "graph_title File age" + echo 'graph_args --base 1000 -l 0' + echo 'graph_vlabel seconds' + echo 'graph_category disk' + + exit 0 + ;; +esac + + +COUNTER=1 +while [ $COUNTER -gt 0 ]; do + FILE_COUNTER="file${COUNTER}_path" + eval FILE=\$$FILE_COUNTER + if [ "$FILE" == "" ]; then + break; + fi + + # If the file isn't readable, say it's zero. + if [ ! -r "$FILE" ]; then + VALUE=0 + else + VALUE=$(($(date +%s) - $(stat -c '%Y' "$FILE"))) + fi + + echo "file_$COUNTER.value $VALUE" + let COUNTER=COUNTER+1 +done; +exit + +# -*- sh -*- + +: << =cut + +=head1 NAME + +file_age - Monitors the age of files. + +=head1 CONFIGURATION + +Since there is no way for the plugin to guess which files you want monitored, you're going to have to set each file up separately. Put the following in a file in your plugin-conf.d directory. + +[file_age] + user root # May not be necessary, depending on which files you want monitored. + + env.file1_path /var/log/syslog # Mandatory, complete path to file. + env.file1_label System syslog # Optional label if you don't want the file name to be displayed. + env.file1_warning 86400 # Optional warning level. Measured in seconds. 86400 is one day of seconds. + env.file1_critical 864000 # Optional critical level. Measured in seconds. + +Continue with file2, file3, etc... + +Here, have some seconds: + + 3600 One hour + 7300 Two hours + 10800 Three hours + 21600 Six hours + 43200 Twelve hours + 86400 One day + 172800 Two days + 259200 Three days + 604800 One week + +=head1 AUTHOR + +Edward Plainview + +=head1 DONATIONS + +If you wish to donate money for this plugin, please read https://it.sverigedemokraterna.se/donera/ + +=head1 LICENSE + +GPLv3 + +=head1 MAGIC MARKERS + + #%# family=auto + +=head1 VERSION + +1.0 released 2012-02-26 + +=cut From 336ca2a97731c2b85880c5a3c3da7f8189cb62ed Mon Sep 17 00:00:00 2001 From: David Bjornsson Date: Tue, 28 Feb 2012 15:26:42 +0000 Subject: [PATCH 107/123] Fixing visibility issues in the utilization function --- plugins/zfs/zfs_stats_ | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/plugins/zfs/zfs_stats_ b/plugins/zfs/zfs_stats_ index a9d9801e..40a4072b 100755 --- a/plugins/zfs/zfs_stats_ +++ b/plugins/zfs/zfs_stats_ @@ -213,25 +213,25 @@ utilization() { echo 'max_size.label Maximum Size' echo 'max_size.draw AREA' - echo 'target_size.label Target Size' - echo 'target_size.draw AREA' echo 'size.label Size' echo 'size.draw AREA' - echo 'recently_size.label Recently Used Cache Size' - echo 'recently_size.draw AREA' - echo 'frequently_size.label Frequently Used Cache Size' - echo 'frequently_size.draw AREA' echo 'min_size.label Minimum Size' - echo 'min_size.draw AREA' + echo 'min_size.draw AREA' + echo 'target_size.label Target Size' + echo 'target_size.draw LINE1' + echo 'recently_size.label Recently Used Cache Size' + echo 'recently_size.draw LINE1' + echo 'frequently_size.label Frequently Used Cache Size' + echo 'frequently_size.draw LINE1' exit 0 else echo 'max_size.value ' $MAX_SIZE - echo 'target_size.value ' $TARGET_SIZE echo 'size.value ' $SIZE + echo 'min_size.value ' $MIN_SIZE + echo 'target_size.value ' $TARGET_SIZE echo 'recently_size.value ' $MRU_SIZE echo 'frequently_size.value ' $MFU_SIZE - echo 'min_size.value ' $MIN_SIZE fi } From b9557dd930d8f6ca7cfb52efc0038a167bb8341e Mon Sep 17 00:00:00 2001 From: Kenyon Ralph Date: Tue, 28 Feb 2012 09:36:19 -0800 Subject: [PATCH 108/123] dovecot: logfile backwards compatibility for Debian Squeeze Authored by Alessandro Fazzi . I just squashed his commits into one commit. --- plugins/mail/dovecot | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/mail/dovecot b/plugins/mail/dovecot index beb62633..03119c26 100755 --- a/plugins/mail/dovecot +++ b/plugins/mail/dovecot @@ -68,7 +68,7 @@ fi # Total Logins ###################### echo -en "login_total.value " -NEW_TOTAL=$(egrep 'dovecot.*Login' $LOGFILE | grep "`date '+%b %e'`" | sort | wc -l) +NEW_TOTAL=$(egrep '[dovecot]?.*Login' $LOGFILE | grep "`date '+%b %e'`" | sort | wc -l) OLD_TOTAL=$(grep TOTAL $STAT_FILE | cut -f2 -d '=') TOTAL=$($EXPR_BIN $NEW_TOTAL - $OLD_TOTAL) if [ $TOTAL -gt 0 ]; then @@ -80,8 +80,8 @@ echo -n ###################### # Connected Users ###################### -DISCONNECTS=$(egrep 'dovecot.*Disconnected' $LOGFILE | sort | wc -l) -CONNECTS=$(egrep 'dovecot.*Login' $LOGFILE | sort | wc -l) +DISCONNECTS=$(egrep '[dovecot]?.*Disconnected' $LOGFILE | sort | wc -l) +CONNECTS=$(egrep '[dovecot]?.*Login' $LOGFILE | sort | wc -l) DISCON=$($EXPR_BIN $CONNECTS - $DISCONNECTS) if [ $DISCON -lt 0 ]; then DISCON=0 @@ -93,7 +93,7 @@ echo -n # TLS Logins ###################### echo -en "login_tls.value " -NEW_TLS=$(egrep 'dovecot.*Login.*TLS' $LOGFILE | grep "`date '+%b %e'`" | sort | wc -l) +NEW_TLS=$(egrep '[dovecot]?.*Login.*TLS' $LOGFILE | grep "`date '+%b %e'`" | sort | wc -l) OLD_TLS=$(grep TLS $STAT_FILE | cut -f2 -d '=') TLS=$($EXPR_BIN $NEW_TLS - $OLD_TLS) if [ $TLS -gt 0 ]; then @@ -106,7 +106,7 @@ echo -n # SSL Logins ###################### echo -en "login_ssl.value " -NEW_SSL=$(egrep 'dovecot.*Login.*SSL' $LOGFILE | grep "`date '+%b %e'`" | sort | wc -l) +NEW_SSL=$(egrep '[dovecot]?.*Login.*SSL' $LOGFILE | grep "`date '+%b %e'`" | sort | wc -l) OLD_SSL=$(grep SSL $STAT_FILE | cut -f2 -d '=') SSL=$($EXPR_BIN $NEW_SSL - $OLD_SSL) if [ $SSL -gt 0 ]; then @@ -119,7 +119,7 @@ echo -n # IMAP Logins ###################### echo -en "login_imap.value " -NEW_IMAP=$(egrep 'dovecot.*imap.*Login' $LOGFILE | grep "`date '+%b %e'`" | sort | wc -l) +NEW_IMAP=$(egrep '[dovecot]?.*imap.*Login' $LOGFILE | grep "`date '+%b %e'`" | sort | wc -l) OLD_IMAP=$(grep IMAP $STAT_FILE | cut -f2 -d '=') IMAP=$($EXPR_BIN $NEW_IMAP - $OLD_IMAP) if [ $IMAP -gt 0 ]; then @@ -132,7 +132,7 @@ echo -n # POP3 Logins ###################### echo -en "login_pop3.value " -NEW_POP3=$(egrep 'dovecot.*pop3.*Login' $LOGFILE | grep "`date '+%b %e'`" | sort | wc -l) +NEW_POP3=$(egrep '[dovecot]?.*pop3.*Login' $LOGFILE | grep "`date '+%b %e'`" | sort | wc -l) OLD_POP3=$(grep POP3 $STAT_FILE | cut -f2 -d '=') POP3=$($EXPR_BIN $NEW_POP3 - $OLD_POP3) if [ $POP3 -gt 0 ]; then From 992d1c8d5ccbf4b7ace672b00af0598f6ac7250c Mon Sep 17 00:00:00 2001 From: Kenyon Ralph Date: Wed, 29 Feb 2012 15:28:16 -0800 Subject: [PATCH 109/123] remove plugin included in main munin distribution --- plugins/network/openvpn_clients | 91 --------------------------------- 1 file changed, 91 deletions(-) delete mode 100755 plugins/network/openvpn_clients diff --git a/plugins/network/openvpn_clients b/plugins/network/openvpn_clients deleted file mode 100755 index 522f5598..00000000 --- a/plugins/network/openvpn_clients +++ /dev/null @@ -1,91 +0,0 @@ -#!/usr/bin/perl -# -# Copyright (C) 2005-2007 Rodolphe Quiedeville -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 dated June, -# 1991. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# If you improve this script please send your version to my email address -# with the copyright notice upgrade with your name. -# -# Munin's plugin to monitor number of clients connected to openvpn server -# -# Usage: copy or link into /etc/munin/plugins -# -# Parameters: -# -# config (required) -# autoconf (optional - used by munin-config) -# -# $Log$ -# Revision 1.2 2007/01/17 15:57:19 rodo -# Correct family -# -# Revision 1.1 2005/10/11 14:12:19 Rodolphe Quiedeville -# -# Magic markers (optinal - used by munin-config and some installation -# scripts): -# -#%# family=auto -#%# capabilities=autoconf - -use strict; - -my $statuslogfile = "/etc/openvpn/openvpn-status.log"; -my $clients = 0; - -if($ARGV[0] and $ARGV[0] eq "autoconf" ) { - if(-f $statuslogfile) { - if(-r $statuslogfile) { - print "yes\n"; - exit 0; - } else { - print "no (logfile not readable)\n"; - } - } else { - print "no (logfile not found)\n"; - } - exit 1; -} - -if ($ARGV[0] and $ARGV[0] eq "config" ){ - print "graph_title OpenVpn\n"; - print "graph_args --base 1000 -l 0\n"; - print "graph_scale yes\n"; - print "graph_vlabel clients\n"; - print "graph_category network\n"; - print "graph_info This graph shows the numbers of clients connected to openvpn server.\n"; - print "clients.label clients\n"; - print "clients.info The number of clients connected to openvpn server\n"; - exit 0; -} - -if (-f "$statuslogfile") { - open(IN, "$statuslogfile") or exit 4; - my $flagu = 0; - while() { - if(/^ROUTING TABLE$/) { - $flagu = 0; - } - if ($flagu) { - $clients = $clients + 1; - } - if(/^Common Name,Real Address,Bytes Received,Bytes Sent,Connected Since$/) { - $flagu = 1; - } - } - close(IN); -} - -print "clients.value " . $clients."\n"; From d055f33780344d06d0673127716ba714aaabe01a Mon Sep 17 00:00:00 2001 From: Kenyon Ralph Date: Wed, 29 Feb 2012 15:44:07 -0800 Subject: [PATCH 110/123] move singleton plugins to the network directory --- plugins/{networker => network}/nsr_device_writing | 0 plugins/{openvpn => network}/openvpn_multi | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename plugins/{networker => network}/nsr_device_writing (100%) rename plugins/{openvpn => network}/openvpn_multi (100%) diff --git a/plugins/networker/nsr_device_writing b/plugins/network/nsr_device_writing similarity index 100% rename from plugins/networker/nsr_device_writing rename to plugins/network/nsr_device_writing diff --git a/plugins/openvpn/openvpn_multi b/plugins/network/openvpn_multi similarity index 100% rename from plugins/openvpn/openvpn_multi rename to plugins/network/openvpn_multi From e70093d387e7e05cb6151dd8af6c424ea80337c8 Mon Sep 17 00:00:00 2001 From: Kenyon Ralph Date: Thu, 1 Mar 2012 06:43:38 -0800 Subject: [PATCH 111/123] remove processes plugin already in main munin distribution --- plugins/processes/processes | 282 ------------------------------------ 1 file changed, 282 deletions(-) delete mode 100755 plugins/processes/processes diff --git a/plugins/processes/processes b/plugins/processes/processes deleted file mode 100755 index c6986646..00000000 --- a/plugins/processes/processes +++ /dev/null @@ -1,282 +0,0 @@ -#!/bin/sh -# -# Copyright (C) 2006 Lars Strand -# -# Munin-plugin to monitor processes on Linux -# FreeBSD, OpenBSD, NetBSD, Solaris and OSX. -# -# Require munin-server version 1.2.5 or 1.3.3 (or higher). -# -# This plugin is backwards compatible with the old processes-plugins -# found on SunOS, Linux and *BSD (i.e. the history is preserved). -# -# All fields have colours associated with them which reflect the type -# of process (sleeping/idle = blue, running = green, -# stopped/zombie/dead = red, etc.) -# -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; version 2 dated June, -# 1991. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# -# Parameters understood: -# -# config (required) -# autoconf (optional - used by munin-config) -# -# $Log$ -# -# -# -# Magick markers (optional - used by munin-config and som installation -# scripts): -# -#%# family=auto -#%# capabilities=autoconf - -# Search for program in $PATH unless predefined. -[ $awk ] || awk="awk" -[ $ps ] || ps="ps" - -# Find operating system -[ $OPERSYS ] || OPERSYS=`uname` || exit 1 - -if [ "$1" = "autoconf" ]; then - echo yes - exit 0 -fi - -# Define colours -RUNNABLE='22ff22' # Green -SLEEPING='0022ff' # Blue -STOPPED='cc0000' # Darker red -ZOMBIE='990000' # Darkest red -UNINTERRUPTIBLE='ffa500' # Orange -IDLE='4169e1' # Royal blue -PAGING='00aaaa' # Darker turquoise -INTERRUPT='ff00ff' # Fuchsia -LOCK='ff3333' # Lighter red -RUNNING='00ff7f' # Spring green -DEAD='ff0000' # Red -SUSPENDED='ff1493' # Deep pink -TOTAL='c0c0c0' # Silver - -# Taken from ps(1) -# R - Linux, SunOS, FreeBSD, OpenBSD, NetBSD, OSX (runable) -# S - Linux, SunOS, FreeBSD*, OpenBSD*, NetBSD*, OSX* (sleeping) -# T - Linux, SunOS, FreeBSD, OpenBSD, NetBSD, OSX (stopped) -# Z - Linux, SunOS, FreeBSD, OpenBSD, NetBSD, OSX (zombie) -# D - Linux, FreeBSD, OpenBSD, NetBSD (uninterruptible) -# I - FreeBSD, OpenBSD, NetBSD, OSX (idle) -# W - Linux*, FreeBSD* (paging/interrupt) -# L - FreeBSD (lock) -# O - SunOS (running) -# X - Linux (dead) -# U - OSX, NetBSD* (uninterruptible/suspended) -# *) Differ meaning - -if [ "$1" = "config" ]; then - echo "graph_title Processes" - echo "graph_info This graph shows the number of processes" - echo "graph_category processes" - echo "graph_args --base 1000 -l 0" - - # OS specific flags - if [ "$OPERSYS" = "Linux" ]; then - echo "graph_order sleeping stopped zombie dead paging uninterruptible runnable processes" - echo "dead.label dead" - echo "dead.draw STACK" - echo "dead.colour $DEAD" - echo "dead.info The number of dead processes." - echo "paging.label paging" - echo "paging.draw STACK" - echo "paging.colour $PAGING" - echo "paging.info The number of paging processes (<2.6 kernels only)." - - elif [ "$OPERSYS" = "SunOS" ]; then - echo "graph_order sleeping stopped zombie runnable running total" - echo "running.label running" - echo "running.draw STACK" - echo "running.colour $RUNNING" - echo "running.info The number of processes that are running on a processor." - # Be backwards compatible. - echo "total.label total" - echo "total.draw LINE1" - echo "total.colour $TOTAL" - echo "total.info The total number of processes." - - elif [ "$OPERSYS" = "FreeBSD" ]; then - echo "graph_order sleeping idle stopped zombie lock uninterruptible interrupt runnable processes" - echo "lock.label lock" - echo "lock.draw STACK" - echo "lock.colour $LOCK" - echo "lock.info The number of processes that are waiting to acquire a lock." - echo "interrupt.label interrupt" - echo "interrupt.draw STACK" - echo "interrupt.colour $INTERRUPT" - echo "interrupt.info The number of idle interrupt threads." - - elif [ "$OPERSYS" = "OpenBSD" ]; then - echo "graph_order sleeping idle stopped zombie uninterruptible runnable processes" - - elif [ "$OPERSYS" = "NetBSD" ]; then - echo "graph_order sleeping idle stopped zombie uninterruptible suspended runnable processes" - echo "suspended.label suspended" - echo "suspended.draw STACK" - echo "suspended.colour $SUSPENDED" - echo "suspended.info The number of processes that are suspended." - - elif [ "$OPERSYS" = "Darwin" ]; then - echo "graph_order sleeping idle stopped zombie uninterruptible running processes" - echo "uninterruptible.label uninterruptible" - echo "uninterruptible.draw STACK" - echo "uninterruptible.colour $UNINTERRUPTIBLE" - echo "uninterruptible.info The number of uninterruptible processes (usually IO)." - fi - - # Common flags for some OS - if [ "$OPERSYS" = "FreeBSD" ] || [ "$OPERSYS" = "OpenBSD" ] || - [ "$OPERSYS" = "NetBSD" ] || [ "$OPERSYS" = "Darwin" ]; then - echo "idle.label idle" - echo "idle.draw STACK" - echo "idle.colour $IDLE" - echo "idle.info The number of processes that are idle (sleeping for longer than about 20 seconds)." - echo "sleeping.label sleeping" - echo "sleeping.draw AREA" - echo "sleeping.colour $SLEEPING" - echo "sleeping.info The number of processes that are sleeping for less than about 20 seconds." - else - echo "sleeping.label sleeping" - echo "sleeping.draw AREA" - echo "sleeping.colour $SLEEPING" - echo "sleeping.info The number of sleeping processes." - fi - - if [ "$OPERSYS" = "Linux" ] || [ "$OPERSYS" = "FreeBSD" ] || - [ "$OPERSYS" = "OpenBSD" ] || [ "$OPERSYS" = "NetBSD" ]; then - echo "uninterruptible.label uninterruptible" - echo "uninterruptible.draw STACK" - echo "uninterruptible.colour $UNINTERRUPTIBLE" - echo "uninterruptible.info The number of uninterruptible processes (usually IO)." - fi - - # Common flags - echo "zombie.label zombie" - echo "zombie.draw STACK" - echo "zombie.colour $ZOMBIE" - echo "zombie.info The number of defunct ("zombie") processes (process terminated and parent not waiting)." - - echo "stopped.label stopped" - echo "stopped.draw STACK" - echo "stopped.colour $STOPPED" - echo "stopped.info The number of stopped or traced processes." - - echo "runnable.label runnable" - echo "runnable.draw STACK" - echo "runnable.colour $RUNNABLE" - echo "runnable.info The number of runnable processes (on the run queue)." - - if [ "$OPERSYS" != "SunOS" ]; then - # Not using 'graph_total' due to backwards compability. SunOS uses 'total'. - #echo 'graph_total total' - echo "processes.label total" - echo "processes.draw LINE1" - echo "processes.colour $TOTAL" - echo "processes.info The total number of processes." - fi - - exit 0 -fi - -if [ "$OPERSYS" = "Linux" ]; then - $ps --no-header -eo s | $awk ' -{ processes++; stat[$1]++ } -END { -print "processes.value " 0+processes; -print "uninterruptible.value " 0+stat["D"]; -print "runnable.value " 0+stat["R"]; -print "sleeping.value " 0+stat["S"]; -print "stopped.value " 0+stat["T"]; -print "paging.value " 0+stat["W"]; -print "dead.value " 0+stat["X"]; -print "zombie.value " 0+stat["Z"]; -}' - -elif [ "$OPERSYS" = "SunOS" ]; then - $ps -e -o s | $awk ' -{ total++; stat[$1]++ } -END { -print "total.value " 0+total; -print "running.value " 0+stat["O"]; -print "sleeping.value " 0+stat["S"]; -print "runnable.value " 0+stat["R"]; -print "stopped.value " 0+stat["T"]; -print "zombie.value " 0+stat["Z"]; -}' -elif [ "$OPERSYS" = "FreeBSD" ]; then - $ps -axo state= | sed -e 's/^\(.\).*/\1/' | $awk ' -{ processes++; stat[$1]++ } -END { -print "processes.value " 0+processes; -print "uninterruptible.value " 0+stat["D"]; -print "idle.value " 0+stat["I"]; -print "lock.value " 0+stat["G"]; -print "runnable.value " 0+stat["R"]; -print "sleeping.value " 0+stat["S"]; -print "stopped.value " 0+stat["T"]; -print "interrupt.value " 0+stat["W"]; -print "zombie.value " 0+stat["Z"]; -}' -elif [ "$OPERSYS" = "OpenBSD" ]; then - # First line is header. Remove it. - $ps -axo state= | sed '1d' | sed -e 's/^\(.\).*/\1/' | $awk ' -{ processes++; stat[$1]++ } -END { -print "processes.value " 0+processes; -print "uninterruptible.value " 0+stat["D"]; -print "idle.value " 0+stat["I"]; -print "runnable.value " 0+stat["R"]; -print "sleeping.value " 0+stat["S"]; -print "stopped.value " 0+stat["T"]; -print "zombie.value " 0+stat["Z"]; -}' -elif [ "$OPERSYS" = "NetBSD" ]; then - # First line is header. Remove it. - $ps -axo state= | sed '1d' | sed -e 's/^\(.\).*/\1/' | $awk ' -{ processes++; stat[$1]++ } -END { -print "processes.value " 0+processes; -print "uninterruptible.value " 0+stat["D"]; -print "idle.value " 0+stat["I"]; -print "suspended.value " 0+stat["U"]; -print "runnable.value " 0+stat["R"]; -print "sleeping.value " 0+stat["S"]; -print "stopped.value " 0+stat["T"]; -print "zombie.value " 0+stat["Z"]; -}' - -elif [ "$OPERSYS" = "Darwin" ]; then - # First line is header. Remove it. - $ps -axo state= | sed '1d' | sed -e 's/^\(.\).*/\1/' | $awk ' -{ processes++; stat[$1]++ } -END { -print "processes.value " 0+processes; -print "uninterruptible.value " 0+stat["U"]; -print "idle.value " 0+stat["I"]; -print "runnable.value " 0+stat["R"]; -print "sleeping.value " 0+stat["S"]; -print "stopped.value " 0+stat["T"]; -print "zombie.value " 0+stat["Z"]; -}' -fi From b0f2565a40e158be34670c3e0cdb009209928bc5 Mon Sep 17 00:00:00 2001 From: Lars Falk-Petersen Date: Thu, 1 Mar 2012 17:03:07 +0100 Subject: [PATCH 112/123] Small bugfixed on cleware. --- plugins/sensors/cleware | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/plugins/sensors/cleware b/plugins/sensors/cleware index ac183cc1..0e26e3b6 100755 --- a/plugins/sensors/cleware +++ b/plugins/sensors/cleware @@ -1,15 +1,26 @@ #!/bin/bash -# Written by Lars Falk-Petersen, cleware@falk-petersen.no -# Version 0.3. To be used with http://www.vanheusden.com/clewarecontrol/ +# Written by Lars Falk-Petersen +# Version 0.4. See https://github.com/ways +# To be used with http://www.vanheusden.com/clewarecontrol/ # Clewarecontrol device serial must be set in config file: # #Put the following lines in /etc/munin/plugin-conf.d/cleware -# [cleware*] -# #device serial. find it by running: clewarecontrol -l -# env.serial 7778 -# #path of clewarecontrol -# env.bin /usr/bin/clewarecontrol +# [cleware*] +# #device serial. find it by running: clewarecontrol -l +# env.serial 7778 +# #path of clewarecontrol +# env.bin /usr/bin/clewarecontrol +# +# Munin must be able to read and write to the device. +# Run the following as root: +# # chgrp munin /dev/usb/hiddev0 +# # chmod g+rw /dev/usb/hiddev0 +# +# If that works, you have to make it permanent. On Ubuntu: +# Create this file: /etc/udev/rules.d/99-hidraw-permissions.rules +# #Allow munin to read hidraws from Cleware +# SUBSYSTEM=="usb", KERNEL=="hiddev*", GROUP="munin", MODE="0660" case $1 in config) @@ -32,11 +43,10 @@ if [ ! $serial ]; then exit 1 fi -$bin -c 1 -ag > /dev/null 2>&1 +$bin -d $serial -c 1 -ag > /dev/null 2>&1 echo -n "temp.value " $bin -d $serial -b -c 1 -rt echo -n "hum.value " $bin -d $serial -b -c 1 -rh - From 933f0ea70b97bd1383b61e95554afa34787555f5 Mon Sep 17 00:00:00 2001 From: Kenyon Ralph Date: Mon, 5 Mar 2012 00:00:56 -0800 Subject: [PATCH 113/123] remove plugin in main munin distribution (jmx), and extract jmx2munin --- plugins/java/jmx2munin/.gitignore | 8 ++ plugins/java/jmx2munin/README.md | 79 ++++++++++++ .../jmx2munin.cfg/cassandra/nodes_in_cluster | 3 + plugins/java/jmx2munin/contrib/jmx2munin.sh | 55 ++++++++ plugins/java/jmx2munin/pom.xml | 121 ++++++++++++++++++ .../src/main/java/org/vafer/jmx/Enums.java | 77 +++++++++++ .../src/main/java/org/vafer/jmx/Filter.java | 9 ++ .../main/java/org/vafer/jmx/ListOutput.java | 26 ++++ .../src/main/java/org/vafer/jmx/NoFilter.java | 10 ++ .../src/main/java/org/vafer/jmx/Output.java | 9 ++ .../src/main/java/org/vafer/jmx/Query.java | 52 ++++++++ .../src/main/java/org/vafer/jmx/Value.java | 52 ++++++++ .../main/java/org/vafer/jmx/munin/Munin.java | 67 ++++++++++ .../jmx/munin/MuninAttributesFilter.java | 24 ++++ .../java/org/vafer/jmx/munin/MuninOutput.java | 93 ++++++++++++++ plugins/{jvm => java}/jstat__gccount | 0 plugins/{jvm => java}/jstat__gctime | 0 plugins/{jvm => java}/jstat__heap | 0 plugins/{jvm => java}/jvm_sun_memory | 0 plugins/{jvm => java}/jvm_sun_minorgcs | 0 plugins/{jvm => java}/jvm_sun_tenuredgcs | 0 plugins/other/jmx | Bin 15002 -> 0 bytes plugins/other/jmx2munin | Bin 12440 -> 0 bytes 23 files changed, 685 insertions(+) create mode 100644 plugins/java/jmx2munin/.gitignore create mode 100644 plugins/java/jmx2munin/README.md create mode 100644 plugins/java/jmx2munin/contrib/jmx2munin.cfg/cassandra/nodes_in_cluster create mode 100644 plugins/java/jmx2munin/contrib/jmx2munin.sh create mode 100644 plugins/java/jmx2munin/pom.xml create mode 100644 plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Enums.java create mode 100644 plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Filter.java create mode 100644 plugins/java/jmx2munin/src/main/java/org/vafer/jmx/ListOutput.java create mode 100644 plugins/java/jmx2munin/src/main/java/org/vafer/jmx/NoFilter.java create mode 100644 plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Output.java create mode 100644 plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Query.java create mode 100644 plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Value.java create mode 100644 plugins/java/jmx2munin/src/main/java/org/vafer/jmx/munin/Munin.java create mode 100644 plugins/java/jmx2munin/src/main/java/org/vafer/jmx/munin/MuninAttributesFilter.java create mode 100644 plugins/java/jmx2munin/src/main/java/org/vafer/jmx/munin/MuninOutput.java rename plugins/{jvm => java}/jstat__gccount (100%) rename plugins/{jvm => java}/jstat__gctime (100%) rename plugins/{jvm => java}/jstat__heap (100%) rename plugins/{jvm => java}/jvm_sun_memory (100%) rename plugins/{jvm => java}/jvm_sun_minorgcs (100%) rename plugins/{jvm => java}/jvm_sun_tenuredgcs (100%) delete mode 100755 plugins/other/jmx delete mode 100755 plugins/other/jmx2munin diff --git a/plugins/java/jmx2munin/.gitignore b/plugins/java/jmx2munin/.gitignore new file mode 100644 index 00000000..0a99f329 --- /dev/null +++ b/plugins/java/jmx2munin/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +.classpath +.project +.fatjar +target +eclipse +old +bin diff --git a/plugins/java/jmx2munin/README.md b/plugins/java/jmx2munin/README.md new file mode 100644 index 00000000..d1899db0 --- /dev/null +++ b/plugins/java/jmx2munin/README.md @@ -0,0 +1,79 @@ +# jmx2munin + +The [jmx2munin](http://github.com/tcurdt/jmx2munin) project exposes JMX MBean attributes to [Munin](http://munin-monitoring.org/). +Some of it's features: + + * strictly complies to the plugin format + * exposes composite types like Lists, Maps, Set as useful as possible + * String values can be mapped to numbers + +# How to use + +This is what the Munin script will call. So you should test this first. Of course with your parameters. This example expose all Cassandra information to Munin. + + java -jar jmx2munin.jar \ + -url service:jmx:rmi:///jndi/rmi://localhost:8080/jmxrmi \ + -query "org.apache.cassandra.*:*" + +The "url" parameters specifies the JMX URL, the query selects the MBeans (and optionally also the attributes) to expose. + + java -jar jmx2munin.jar \ + -url service:jmx:rmi:///jndi/rmi://localhost:8080/jmxrmi \ + -query "org.apache.cassandra.*:*" \ + -attribute org_apache_cassandra_db_storageservice_livenodes_size + +The script that does the actual interaction with munin you can find in the contrib section. It's the one you should link in the your Munin plugin directory. + + :/etc/munin/plugins$ ls -la cassandra_* + lrwxrwxrwx 1 root root 37 2011-04-07 19:58 cassandra_nodes_in_cluster -> /usr/share/munin/plugins/jmx2munin.sh + +In the plugin conf you point to the correct configuration + + [cassandra_*] + env.query org.apache.cassandra.*:* + + [cassandra_nodes_in_cluster] + env.config cassandra/nodes_in_cluster + +A possible configuration could look like this + + graph_title Number of Nodes in Cluster + graph_vlabel org_apache_cassandra_db_storageservice_livenodes_size + org_apache_cassandra_db_storageservice_livenodes_size.label number of nodes + +The script will extract the attributes from the config and caches the JMX results to reduce the load when showing many values. + +# More advanced + +Sometimes it can be useful to track String values by mapping them into an enum as they really describe states. To find this possible candidates you can call: + + java -jar jmx2munin.jar \ + -url service:jmx:rmi:///jndi/rmi://localhost:8080/jmxrmi \ + -query "org.apache.cassandra.*:*" \ + list + +It should output a list of possible candidates. This can now be turned into a enum configuration file: + + [org.apache.cassandra.db.StorageService:OperationMode] + 0 = ^Normal + 1 = ^Client + 2 = ^Joining + 3 = ^Bootstrapping + 4 = ^Leaving + 5 = ^Decommissioned + 6 = ^Starting drain + 7 = ^Node is drained + +Which you then can provide: + + java -jar jmx2munin.jar \ + -url service:jmx:rmi:///jndi/rmi://localhost:8080/jmxrmi \ + -query "org.apache.cassandra.*:*" \ + -enums /path/to/enums.cfg + +Now matching values get replaced by their numerical representation. On the left needs to be a unique number on the right side is a regular expression. If a string cannot be matched according to the spec "U" for "undefined" will be returned. + +# License + +Licensed under the Apache License, Version 2.0 (the "License") +You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 diff --git a/plugins/java/jmx2munin/contrib/jmx2munin.cfg/cassandra/nodes_in_cluster b/plugins/java/jmx2munin/contrib/jmx2munin.cfg/cassandra/nodes_in_cluster new file mode 100644 index 00000000..7fe323e3 --- /dev/null +++ b/plugins/java/jmx2munin/contrib/jmx2munin.cfg/cassandra/nodes_in_cluster @@ -0,0 +1,3 @@ +graph_title Number of Nodes in Cluster +graph_vlabel org_apache_cassandra_db_storageservice_livenodes_size +org_apache_cassandra_db_storageservice_livenodes_size.label number of nodes diff --git a/plugins/java/jmx2munin/contrib/jmx2munin.sh b/plugins/java/jmx2munin/contrib/jmx2munin.sh new file mode 100644 index 00000000..2ccb1841 --- /dev/null +++ b/plugins/java/jmx2munin/contrib/jmx2munin.sh @@ -0,0 +1,55 @@ +#!/bin/bash +# [cassandra_nodes_in_cluster] +# env.config cassandra/nodes_in_cluster +# env.query org.apache.cassandra.*:* + +if [ -z "$MUNIN_LIBDIR" ]; then + MUNIN_LIBDIR="`dirname $(dirname "$0")`" +fi + +if [ -f "$MUNIN_LIBDIR/plugins/plugin.sh" ]; then + . $MUNIN_LIBDIR/plugins/plugin.sh +fi + +if [ "$1" = "autoconf" ]; then + echo yes + exit 0 +fi + +if [ -z "$url" ]; then + # this is very common so make it a default + url="service:jmx:rmi:///jndi/rmi://127.0.0.1:8080/jmxrmi" +fi + +if [ -z "$config" -o -z "$query" -o -z "$url" ]; then + echo "Configuration needs attributes config, query and optinally url" + exit 1 +fi + +JMX2MUNIN_DIR="$MUNIN_LIBDIR/plugins" +CONFIG="$JMX2MUNIN_DIR/jmx2munin.cfg/$config" + +if [ "$1" = "config" ]; then + cat "$CONFIG" + exit 0 +fi + +JAR="$JMX2MUNIN_DIR/jmx2munin.jar" +CACHED="/tmp/jmx2munin" + +if test ! -f $CACHED || test `find "$CACHED" -mmin +2`; then + + java -jar "$JAR" \ + -url "$url" \ + -query "$query" \ + $ATTRIBUTES \ + > $CACHED + + echo "cached.value `date +%s`" >> $CACHED +fi + +ATTRIBUTES=`awk '/\.label/ { gsub(/\.label/,""); print $1 }' $CONFIG` + +for ATTRIBUTE in $ATTRIBUTES; do + grep $ATTRIBUTE $CACHED +done \ No newline at end of file diff --git a/plugins/java/jmx2munin/pom.xml b/plugins/java/jmx2munin/pom.xml new file mode 100644 index 00000000..2bbbd026 --- /dev/null +++ b/plugins/java/jmx2munin/pom.xml @@ -0,0 +1,121 @@ + + + 4.0.0 + + org.vafer + jmx2munin + jmx2munin + 1.0 + + Munin plugin to access JMX information + + http://github.com/tcurdt/jmx2munin + + + + tcurdt + Torsten Curdt + tcurdt at vafer.org + +1 + + + + + + Apache License 2 + http://www.apache.org/licenses/LICENSE-2.0.txt + + + + + scm:git:git://github.com:tcurdt/jmx2munin.git + scm:git:git://github.com:tcurdt/jmx2munin.git + http://github.com/tcurdt/jmx2munin/tree/master + + + + + com.beust + jcommander + 1.17 + + + + junit + junit + 4.5 + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.5 + 1.5 + UTF-8 + + + + org.apache.maven.plugins + maven-surefire-plugin + + never + + **/*TestCase.java + + + **/Abstract* + + true + false + + + + org.apache.maven.plugins + maven-source-plugin + 2.1 + + true + + + + create-source-jar + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-shade-plugin + 1.4 + + + package + + shade + + + false + + + com.beust:jcommander + + + + + org.vafer.jmx.munin.Munin + + + + + + + + + diff --git a/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Enums.java b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Enums.java new file mode 100644 index 00000000..ab5b4831 --- /dev/null +++ b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Enums.java @@ -0,0 +1,77 @@ +package org.vafer.jmx; + +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.TreeMap; +import java.util.regex.Pattern; + +import javax.management.ObjectName; + +public final class Enums { + + private TreeMap> sections = new TreeMap>(); + + public boolean load(String filePath) throws IOException { + BufferedReader input = null; + LinkedHashMap section = new LinkedHashMap(); + try { + input = new BufferedReader(new InputStreamReader(new FileInputStream(filePath))); + String line; + int linenr = 0; + while((line = input.readLine()) != null) { + linenr += 1; + line = line.trim(); + if (line.startsWith("#")) { + continue; + } + if (line.startsWith("[") && line.endsWith("]")) { + // new section + String id = line.substring(1, line.length() - 1); + section = new LinkedHashMap(); + sections.put(id, section); + } else { + String[] pair = line.split("="); + if (pair.length == 2) { + Integer number = Integer.parseInt(pair[0].trim()); + Pattern pattern = Pattern.compile(pair[1].trim()); + if (section.put(number, pattern) != null) { + System.err.println("Line " + linenr + ": previous definitions of " + number); + } + } + } + } + } finally { + if (input != null) { + input.close(); + } + } + return false; + } + + public static String id(ObjectName beanName, String attributeName) { + StringBuilder sb = new StringBuilder(); + sb.append(beanName.getDomain()); + sb.append('.'); + sb.append(beanName.getKeyProperty("type")); + sb.append(':'); + sb.append(attributeName); + return sb.toString(); + } + + public Number resolve(String id, String value) { + LinkedHashMap section = sections.get(id); + if (section == null) { + return null; + } + for(Map.Entry entry : section.entrySet()) { + if (entry.getValue().matcher(value).matches()) { + return entry.getKey(); + } + } + return null; + } +} diff --git a/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Filter.java b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Filter.java new file mode 100644 index 00000000..e7d67a8a --- /dev/null +++ b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Filter.java @@ -0,0 +1,9 @@ +package org.vafer.jmx; + +import javax.management.ObjectName; + +public interface Filter { + + public boolean include(ObjectName bean, String attribute); + +} diff --git a/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/ListOutput.java b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/ListOutput.java new file mode 100644 index 00000000..4e050faf --- /dev/null +++ b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/ListOutput.java @@ -0,0 +1,26 @@ +package org.vafer.jmx; + +import java.util.HashSet; +import java.util.Set; + +import javax.management.ObjectName; + +public final class ListOutput implements Output { + + private final Set seen = new HashSet(); + + public void output(ObjectName beanName, String attributeName, Object value) { + Value.flatten(beanName, attributeName, value, new Value.Listener() { + public void value(ObjectName beanName, String attributeName, String value) { + final String id = Enums.id(beanName, attributeName); + if (!seen.contains(id)) { + System.out.println("[" + id + "]"); + seen.add(id); + } + } + public void value(ObjectName beanName, String attributeName, Number value) { + } + }); + } + +} diff --git a/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/NoFilter.java b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/NoFilter.java new file mode 100644 index 00000000..6188d5c7 --- /dev/null +++ b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/NoFilter.java @@ -0,0 +1,10 @@ +package org.vafer.jmx; + +import javax.management.ObjectName; + +public final class NoFilter implements Filter { + + public boolean include(ObjectName bean, String attribute) { + return true; + } +} diff --git a/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Output.java b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Output.java new file mode 100644 index 00000000..eb9e6ca2 --- /dev/null +++ b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Output.java @@ -0,0 +1,9 @@ +package org.vafer.jmx; + +import javax.management.ObjectName; + +public interface Output { + + public void output(ObjectName beanName, String attributeName, Object value); + +} diff --git a/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Query.java b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Query.java new file mode 100644 index 00000000..e27bc4f5 --- /dev/null +++ b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Query.java @@ -0,0 +1,52 @@ +package org.vafer.jmx; + +import java.io.IOException; +import java.util.Collection; + +import javax.management.AttributeNotFoundException; +import javax.management.InstanceNotFoundException; +import javax.management.IntrospectionException; +import javax.management.MBeanAttributeInfo; +import javax.management.MBeanException; +import javax.management.MBeanInfo; +import javax.management.MBeanServerConnection; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectInstance; +import javax.management.ObjectName; +import javax.management.ReflectionException; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXServiceURL; + +public final class Query { + + public void run(String url, String expression, Filter filter, Output output) throws IOException, MalformedObjectNameException, InstanceNotFoundException, ReflectionException, IntrospectionException, AttributeNotFoundException, MBeanException { + JMXConnector connector = JMXConnectorFactory.connect(new JMXServiceURL(url)); + MBeanServerConnection connection = connector.getMBeanServerConnection(); + final Collection mbeans = connection.queryMBeans(new ObjectName(expression), null); + + for(ObjectInstance mbean : mbeans) { + final ObjectName mbeanName = mbean.getObjectName(); + final MBeanInfo mbeanInfo = connection.getMBeanInfo(mbeanName); + final MBeanAttributeInfo[] attributes = mbeanInfo.getAttributes(); + for (final MBeanAttributeInfo attribute : attributes) { + if (attribute.isReadable()) { + if (filter.include(mbeanName, attribute.getName())) { + final String attributeName = attribute.getName(); + try { + output.output( + mbean.getObjectName(), + attributeName, + connection.getAttribute(mbeanName, attributeName) + ); + } catch(Exception e) { + // System.err.println("Failed to read " + mbeanName + "." + attributeName); + } + } + } + } + + } + connector.close(); + } +} diff --git a/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Value.java b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Value.java new file mode 100644 index 00000000..87af5f8a --- /dev/null +++ b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/Value.java @@ -0,0 +1,52 @@ +package org.vafer.jmx; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.management.ObjectName; + +public final class Value { + + public interface Listener { + public void value(ObjectName beanName, String attributeName, String value); + public void value(ObjectName beanName, String attributeName, Number value); + } + + public static void flatten(ObjectName beanName, String attributeName, Object value, Listener listener) { + if (value instanceof Number) { + + listener.value(beanName, attributeName, (Number) value); + + } else if (value instanceof String) { + + listener.value(beanName, attributeName, (String) value); + + } else if (value instanceof Set) { + + final Set set = (Set) value; + flatten(beanName, attributeName + ".size", set.size(), listener); + for(Object entry : set) { + flatten(beanName, attributeName + "[" + entry + "]", 1, listener); + } + + } else if (value instanceof List) { + + final List list = (List)value; + listener.value(beanName, attributeName + ".size", list.size()); + for(int i = 0; i map = (Map) value; + listener.value(beanName, attributeName + ".size", map.size()); + for(Map.Entry entry : map.entrySet()) { + flatten(beanName, attributeName + "[" + entry.getKey() + "]", entry.getValue(), listener); + } + + } else { + // System.err.println("Failed to convert " + beanName + "." + attributeName); + } + } +} diff --git a/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/munin/Munin.java b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/munin/Munin.java new file mode 100644 index 00000000..9f1ffdc7 --- /dev/null +++ b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/munin/Munin.java @@ -0,0 +1,67 @@ +package org.vafer.jmx.munin; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import org.vafer.jmx.Enums; +import org.vafer.jmx.Filter; +import org.vafer.jmx.ListOutput; +import org.vafer.jmx.NoFilter; +import org.vafer.jmx.Query; + +import com.beust.jcommander.JCommander; +import com.beust.jcommander.Parameter; + +public final class Munin { + + @Parameter(description = "") + private List args = new ArrayList(); + + @Parameter(names = "-url", description = "jmx url", required = true) + private String url; + + @Parameter(names = "-query", description = "query expression", required = true) + private String query; + + @Parameter(names = "-enums", description = "file string to enum config") + private String enumsPath; + + @Parameter(names = "-attribute", description = "attributes to return") + private List attributes = new ArrayList(); + + private void run() throws Exception { + final Filter filter; + if (attributes == null || attributes.isEmpty()) { + filter = new NoFilter(); + } else { + filter = new MuninAttributesFilter(attributes); + } + + final Enums enums = new Enums(); + if (enumsPath != null) { + enums.load(enumsPath); + } + + final String cmd = args.toString().toLowerCase(Locale.US); + if ("[list]".equals(cmd)) { + new Query().run(url, query, filter, new ListOutput()); + } else { + new Query().run(url, query, filter, new MuninOutput(enums)); + } + } + + public static void main(String[] args) throws Exception { + Munin m = new Munin(); + + JCommander cli = new JCommander(m); + try { + cli.parse(args); + } catch(Exception e) { + cli.usage(); + System.exit(1); + } + + m.run(); + } +} diff --git a/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/munin/MuninAttributesFilter.java b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/munin/MuninAttributesFilter.java new file mode 100644 index 00000000..e1a49e83 --- /dev/null +++ b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/munin/MuninAttributesFilter.java @@ -0,0 +1,24 @@ +package org.vafer.jmx.munin; + +import java.util.HashSet; +import java.util.List; + +import javax.management.ObjectName; + +import org.vafer.jmx.Filter; + +public final class MuninAttributesFilter implements Filter { + + private final HashSet attributes = new HashSet(); + + public MuninAttributesFilter(List pAttributes) { + for (String attribute : pAttributes) { + attributes.add(attribute.trim().replaceAll("_size$", "")); + } + } + + public boolean include(ObjectName bean, String attribute) { + return attributes.contains(MuninOutput.attributeName(bean, attribute)); + } + +} diff --git a/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/munin/MuninOutput.java b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/munin/MuninOutput.java new file mode 100644 index 00000000..9fb50b12 --- /dev/null +++ b/plugins/java/jmx2munin/src/main/java/org/vafer/jmx/munin/MuninOutput.java @@ -0,0 +1,93 @@ +package org.vafer.jmx.munin; + +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Hashtable; +import java.util.Locale; + +import javax.management.ObjectName; + +import org.vafer.jmx.Enums; +import org.vafer.jmx.Output; +import org.vafer.jmx.Value; + +public final class MuninOutput implements Output { + + private final Enums enums; + + public MuninOutput(Enums enums) { + this.enums = enums; + } + + public static String attributeName(ObjectName bean, String attribute) { + StringBuilder sb = new StringBuilder(); + sb.append(fieldname(beanString(bean))); + sb.append('_'); + sb.append(fieldname(attribute)); + return sb.toString().toLowerCase(Locale.US); + } + + private static String fieldname(String s) { + return s.replaceAll("[^A-Za-z0-9]", "_"); + } + + private static String beanString(ObjectName beanName) { + StringBuilder sb = new StringBuilder(); + sb.append(beanName.getDomain()); + + Hashtable properties = beanName.getKeyPropertyList(); + + String keyspace = "keyspace"; + if (properties.containsKey(keyspace)) { + sb.append('.'); + sb.append(properties.get(keyspace)); + properties.remove(keyspace); + } + + String type = "type"; + if (properties.containsKey(type)) { + sb.append('.'); + sb.append(properties.get(type)); + properties.remove(type); + } + + ArrayList keys = new ArrayList(properties.keySet()); + Collections.sort(keys); + + for(String key : keys) { + sb.append('.'); + sb.append(properties.get(key)); + } + + return sb.toString(); + // return beanName.getCanonicalName(); + } + + public void output(ObjectName beanName, String attributeName, Object value) { + Value.flatten(beanName, attributeName, value, new Value.Listener() { + public void value(ObjectName beanName, String attributeName, String value) { + final Number v = enums.resolve(Enums.id(beanName, attributeName), value); + if (v != null) { + value(beanName, attributeName, v); + } else { + value(beanName, attributeName, Double.NaN); + } + } + public void value(ObjectName beanName, String attributeName, Number value) { + final String v; + + if (Double.isNaN(value.doubleValue())) { + v = "U"; + } else { + final NumberFormat f = NumberFormat.getInstance(); + f.setMaximumFractionDigits(2); + f.setGroupingUsed(false); + v = f.format(value); + } + + System.out.println(attributeName(beanName, attributeName) + ".value " + v); + } + }); + } +} \ No newline at end of file diff --git a/plugins/jvm/jstat__gccount b/plugins/java/jstat__gccount similarity index 100% rename from plugins/jvm/jstat__gccount rename to plugins/java/jstat__gccount diff --git a/plugins/jvm/jstat__gctime b/plugins/java/jstat__gctime similarity index 100% rename from plugins/jvm/jstat__gctime rename to plugins/java/jstat__gctime diff --git a/plugins/jvm/jstat__heap b/plugins/java/jstat__heap similarity index 100% rename from plugins/jvm/jstat__heap rename to plugins/java/jstat__heap diff --git a/plugins/jvm/jvm_sun_memory b/plugins/java/jvm_sun_memory similarity index 100% rename from plugins/jvm/jvm_sun_memory rename to plugins/java/jvm_sun_memory diff --git a/plugins/jvm/jvm_sun_minorgcs b/plugins/java/jvm_sun_minorgcs similarity index 100% rename from plugins/jvm/jvm_sun_minorgcs rename to plugins/java/jvm_sun_minorgcs diff --git a/plugins/jvm/jvm_sun_tenuredgcs b/plugins/java/jvm_sun_tenuredgcs similarity index 100% rename from plugins/jvm/jvm_sun_tenuredgcs rename to plugins/java/jvm_sun_tenuredgcs diff --git a/plugins/other/jmx b/plugins/other/jmx deleted file mode 100755 index 0f4820b224ba5f34b182330e99497ad827ca0956..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15002 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 From ddc46f88fd5f67204e1c65bdb665991e43d767e1 Mon Sep 17 00:00:00 2001 From: Kenyon Ralph Date: Mon, 5 Mar 2012 00:18:19 -0800 Subject: [PATCH 114/123] readd jmx (extracted) because it seems to be different from the jmx in the main munin distribution --- plugins/java/jmx/examples/java/java_cpu.conf | 22 ++++++ .../examples/java/java_process_memory.conf | 46 ++++++++++++ .../java/jmx/examples/java/java_threads.conf | 14 ++++ .../examples/tomcat/catalina_requests.conf | 16 +++++ .../jmx/examples/tomcat/catalina_threads.conf | 12 ++++ .../jmx/examples/tomcat/catalina_times.conf | 32 +++++++++ .../jmx/examples/tomcat/catalina_traffic.conf | 18 +++++ plugins/java/jmx/plugin/jmx_ | 54 ++++++++++++++ plugins/java/jmx/plugin/jmxquery.jar | Bin 0 -> 8977 bytes plugins/java/jmx/readme.txt | 66 ++++++++++++++++++ 10 files changed, 280 insertions(+) create mode 100644 plugins/java/jmx/examples/java/java_cpu.conf create mode 100644 plugins/java/jmx/examples/java/java_process_memory.conf create mode 100644 plugins/java/jmx/examples/java/java_threads.conf create mode 100644 plugins/java/jmx/examples/tomcat/catalina_requests.conf create mode 100644 plugins/java/jmx/examples/tomcat/catalina_threads.conf create mode 100644 plugins/java/jmx/examples/tomcat/catalina_times.conf create mode 100644 plugins/java/jmx/examples/tomcat/catalina_traffic.conf create mode 100755 plugins/java/jmx/plugin/jmx_ create mode 100644 plugins/java/jmx/plugin/jmxquery.jar create mode 100644 plugins/java/jmx/readme.txt diff --git a/plugins/java/jmx/examples/java/java_cpu.conf b/plugins/java/jmx/examples/java/java_cpu.conf new file mode 100644 index 00000000..1e56cd34 --- /dev/null +++ b/plugins/java/jmx/examples/java/java_cpu.conf @@ -0,0 +1,22 @@ +graph_args --upper-limit 100 -l 0 +graph_scale no +graph_title CPU Usage +graph_vlabel 1000* CPU time % +graph_category Java +graph_order java_cpu_time java_cpu_user_time + +java_cpu_time.label cpu +java_cpu_time.jmxObjectName java.lang:type=Threading +java_cpu_time.jmxAttributeName CurrentThreadCpuTime +java_cpu_time.type DERIVE +java_cpu_time.min 0 +java_cpu_time.graph yes +java_cpu_time.cdef java_cpu_time,3000000,/ + +java_cpu_user_time.label user +java_cpu_user_time.jmxObjectName java.lang:type=Threading +java_cpu_user_time.jmxAttributeName CurrentThreadUserTime +java_cpu_user_time.type DERIVE +java_cpu_user_time.min 0 +java_cpu_user_time.graph yes +java_cpu_user_time.cdef java_cpu_user_time,3000000,/ \ No newline at end of file diff --git a/plugins/java/jmx/examples/java/java_process_memory.conf b/plugins/java/jmx/examples/java/java_process_memory.conf new file mode 100644 index 00000000..ddbdc4d0 --- /dev/null +++ b/plugins/java/jmx/examples/java/java_process_memory.conf @@ -0,0 +1,46 @@ +graph_title Process Memory +graph_vlabel Bytes +graph_category Java +graph_order java_memory_nonheap_committed java_memory_nonheap_max java_memory_nonheap_used java_memory_heap_committed java_memory_heap_max java_memory_heap_used os_memory_physical os_memory_vm + +java_memory_nonheap_committed.label non-heap committed +java_memory_nonheap_committed.jmxObjectName java.lang:type=Memory +java_memory_nonheap_committed.jmxAttributeName NonHeapMemoryUsage +java_memory_nonheap_committed.jmxAttributeKey committed + +java_memory_nonheap_max.label non-heap max +java_memory_nonheap_max.jmxObjectName java.lang:type=Memory +java_memory_nonheap_max.jmxAttributeName NonHeapMemoryUsage +java_memory_nonheap_max.jmxAttributeKey max + +java_memory_nonheap_used.label non-heap used +java_memory_nonheap_used.jmxObjectName java.lang:type=Memory +java_memory_nonheap_used.jmxAttributeName NonHeapMemoryUsage +java_memory_nonheap_used.jmxAttributeKey used + +java_memory_heap_committed.label heap committed +java_memory_heap_committed.jmxObjectName java.lang:type=Memory +java_memory_heap_committed.jmxAttributeName HeapMemoryUsage +java_memory_heap_committed.jmxAttributeKey committed + +java_memory_heap_max.label heap max +java_memory_heap_max.jmxObjectName java.lang:type=Memory +java_memory_heap_max.jmxAttributeName HeapMemoryUsage +java_memory_heap_max.jmxAttributeKey max + +java_memory_heap_used.label heap used +java_memory_heap_used.jmxObjectName java.lang:type=Memory +java_memory_heap_used.jmxAttributeName HeapMemoryUsage +java_memory_heap_used.jmxAttributeKey used + +os_memory_physical.label os free mem +os_memory_physical.jmxObjectName java.lang:type=OperatingSystem +os_memory_physical.jmxAttributeName FreePhysicalMemorySize +os_memory_physical.graph no + +os_memory_vm.label os vmem committed +os_memory_vm.jmxObjectName java.lang:type=OperatingSystem +os_memory_vm.jmxAttributeName CommittedVirtualMemorySize +os_memory_vm.graph no + + diff --git a/plugins/java/jmx/examples/java/java_threads.conf b/plugins/java/jmx/examples/java/java_threads.conf new file mode 100644 index 00000000..f8b64cd4 --- /dev/null +++ b/plugins/java/jmx/examples/java/java_threads.conf @@ -0,0 +1,14 @@ +graph_title Thread Count +graph_vlabel Thread Count +graph_category Java +graph_order java_thread_count java_thread_count_peak + +java_thread_count.label count +java_thread_count.jmxObjectName java.lang:type=Threading +java_thread_count.jmxAttributeName ThreadCount + +java_thread_count_peak.label peak +java_thread_count_peak.jmxObjectName java.lang:type=Threading +java_thread_count_peak.jmxAttributeName PeakThreadCount + + diff --git a/plugins/java/jmx/examples/tomcat/catalina_requests.conf b/plugins/java/jmx/examples/tomcat/catalina_requests.conf new file mode 100644 index 00000000..acb48175 --- /dev/null +++ b/plugins/java/jmx/examples/tomcat/catalina_requests.conf @@ -0,0 +1,16 @@ +graph_title Requests Per Second +graph_vlabel requests per second +graph_category Tomcat +graph_order catalina_request_count catalina_error_count + +catalina_request_count.label requests +catalina_request_count.jmxObjectName Catalina:name=http-8080,type=GlobalRequestProcessor +catalina_request_count.jmxAttributeName requestCount +catalina_request_count.type DERIVE +catalina_request_count.min 0 + +catalina_error_count.label errors +catalina_error_count.jmxObjectName Catalina:name=http-8080,type=GlobalRequestProcessor +catalina_error_count.jmxAttributeName errorCount +catalina_error_count.type DERIVE +catalina_error_count.min 0 diff --git a/plugins/java/jmx/examples/tomcat/catalina_threads.conf b/plugins/java/jmx/examples/tomcat/catalina_threads.conf new file mode 100644 index 00000000..32ab44c2 --- /dev/null +++ b/plugins/java/jmx/examples/tomcat/catalina_threads.conf @@ -0,0 +1,12 @@ +graph_title Thread Count +graph_vlabel Thread Count +graph_category Tomcat +graph_order catalina_threads_count catalina_threads_busy + +catalina_threads_busy.label busy +catalina_threads_busy.jmxObjectName Catalina:name=http-8080,type=ThreadPool +catalina_threads_busy.jmxAttributeName currentThreadsBusy + +catalina_threads_count.label current +catalina_threads_count.jmxObjectName Catalina:name=http-8080,type=ThreadPool +catalina_threads_count.jmxAttributeName currentThreadCount diff --git a/plugins/java/jmx/examples/tomcat/catalina_times.conf b/plugins/java/jmx/examples/tomcat/catalina_times.conf new file mode 100644 index 00000000..e3790259 --- /dev/null +++ b/plugins/java/jmx/examples/tomcat/catalina_times.conf @@ -0,0 +1,32 @@ +graph_title Response Time +graph_vlabel Time, ms +graph_category Rules Engine +graph_args --upper-limit 100 -l 0 +graph_scale no +graph_category Tomcat +graph_order catalina_request_count catalina_max_time catalina_proc_time catalina_proc_tpr + +catalina_request_count.label requests +catalina_request_count.jmxObjectName Catalina:name=http-8080,type=GlobalRequestProcessor +catalina_request_count.jmxAttributeName requestCount +catalina_request_count.graph no +catalina_request_count.type DERIVE +catalina_request_count.min 0 + +catalina_proc_time.label time +catalina_proc_time.jmxObjectName Catalina:name=http-8080,type=GlobalRequestProcessor +catalina_proc_time.jmxAttributeName processingTime +catalina_proc_time.type DERIVE +catalina_proc_time.min 0 +catalina_proc_time.graph no + +catalina_proc_tpr.label avg +catalina_proc_tpr.jmxObjectName Catalina:name=http-8080,type=GlobalRequestProcessor +catalina_proc_tpr.jmxAttributeName processingTime +catalina_proc_tpr.cdef catalina_request_count,0,EQ,0,catalina_proc_time,catalina_request_count,/,IF + +catalina_max_time.label peak +catalina_max_time.jmxObjectName Catalina:name=http-8080,type=GlobalRequestProcessor +catalina_max_time.jmxAttributeName maxTime + + diff --git a/plugins/java/jmx/examples/tomcat/catalina_traffic.conf b/plugins/java/jmx/examples/tomcat/catalina_traffic.conf new file mode 100644 index 00000000..f6d9498d --- /dev/null +++ b/plugins/java/jmx/examples/tomcat/catalina_traffic.conf @@ -0,0 +1,18 @@ +graph_title Traffic +graph_vlabel Bytes rec(-)/sent(+) per second +graph_category Tomcat +graph_order catalina_bytes_received catalina_bytes_sent + +catalina_bytes_sent.label bps +catalina_bytes_sent.jmxObjectName Catalina:name=http-8080,type=GlobalRequestProcessor +catalina_bytes_sent.jmxAttributeName bytesSent +catalina_bytes_sent.type DERIVE +catalina_bytes_sent.min 0 +catalina_bytes_sent.negative catalina_bytes_received + +catalina_bytes_received.label received +catalina_bytes_received.jmxObjectName Catalina:name=http-8080,type=GlobalRequestProcessor +catalina_bytes_received.jmxAttributeName bytesReceived +catalina_bytes_received.type DERIVE +catalina_bytes_received.min 0 +catalina_bytes_received.graph no \ No newline at end of file diff --git a/plugins/java/jmx/plugin/jmx_ b/plugins/java/jmx/plugin/jmx_ new file mode 100755 index 00000000..9fe33f2f --- /dev/null +++ b/plugins/java/jmx/plugin/jmx_ @@ -0,0 +1,54 @@ +#!/bin/sh +# +# Wildcard-plugin to monitor Java JMX (http://java.sun.com/jmx)attributes. +# To monitor a # specific set of JMX attributes, +# link to this file. E.g. +# +# ln -s /usr/share/plugins/jmx_ /etc/munin/plugins/jmx_java_threads +# + +# ...will monitor Java thread count, assuming java_threads.conf file is located in +# /etc/munin/plugins folder. +# +# For Java process to be monitored, it must expose JMX remote interface. +# With Java 1.5 it can be done by adding parameters as: +# +# -Dcom.sun.management.jmxremote.port= -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false +# +# For Tomcat to be monitored, add the following line in catalina.bat script: +# set JAVA_OPTS=%JAVA_OPTS% -Dcom.sun.management.jmxremote.port= -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false +# +# By default, the plugin monitors localhost on = 1616 using URL +# service:jmx:rmi:///jndi/rmi://localhost:1616/jmxrmi +# It can be changed by specifying parameters in /etc/munin/plugin-conf.d/munin-node +# +# [jmx_*] +# env.jmxurl service:jmx:rmi:///jndi/rmi://localhost:1616/jmxrmi +# +# Read more on JMX configuring at http://java.sun.com/j2se/1.5.0/docs/guide/management/agent.html +# $Log$ +# +LINK=`readlink $0` +CONFIGNAME=`basename $0 | sed 's/^jmx_//g'`.conf +RDIR=`dirname $LINK` + +if [ "$jmxurl" = "" ]; then +SERVICE=service:jmx:rmi:///jndi/rmi://localhost:1616/jmxrmi +else +SERVICE="$jmxurl" +fi + +if [ "$jmxuser" != "" ]; then +CREDS="--user=$jmxuser --pass=$jmxpass" +fi + +# checks +test -z $CONFIGNAME || test -z "$RDIR" && exit 1 + +JMXQUERY="java -cp $RDIR/jmxquery.jar org.munin.JMXQuery --url=$SERVICE $CREDS --conf=$RDIR/$CONFIGNAME" + + +case $1 in + (config) $JMXQUERY config;; + (*) $JMXQUERY ;; +esac diff --git a/plugins/java/jmx/plugin/jmxquery.jar b/plugins/java/jmx/plugin/jmxquery.jar new file mode 100644 index 0000000000000000000000000000000000000000..5c37d4bdec25e453d0891a5d12d899f30cecd157 GIT binary patch literal 8977 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/bin/jconsole.exe and connect to +the host/port you setup in your Java process. + +Some examples are: +* standard Java JMX implementation exposes memory, threads, OS, garbage collector parameters +* Tomcat exposes multiple parameters - requests, processing time, threads, etc.. +* spring framework allows to expose Java beans parameters to JMX +* your application may expose any attributes for JMX by declaration or explicitly. +* can monitor localhost or remote processes + +-------- Installation --------- + +Pre-requsisites are: +- installed munin-node +- Java version 5 JRE + +1) Files from "plugin" folder must be copied to /usr/share/munin/plugins (or another - where your munin plugins located) +2) Make sure that jmx_ executable : chmod a+x /usr/share/munin/plugins/jmx_ +3) Copy configuration files that you want to use, from "examples" folder, into /usr/share/munin/plugins folder +4) create links from the /etc/munin/plugins folder to the /usr/share/munin/plugins/jmx_ +The name of the link must follow wildcard pattern: +jmx_, +where configname is the name of the configuration (config filename without extension), for example: +ln -s /usr/share/munin/plugins/jmx_ /etc/munin/plugins/jmx_process_memory +5) optionally specify the environment variable for JMX URL. The default URL corresponds to localhost:1616. +If you have different port listening by JMX or different hostname to monitor, specify jmxurl parameter +in /etc/munin/plugin-conf.d/munin-node: + +[jmx_*] +env.jmxurl service:jmx:rmi:///jndi/rmi://localhost:1616/jmxrmi + +-------- Check Installation --------- + +To check that all installed properly, try invoke plugins from command line, using links like: + +root@re:/etc/munin/plugins# ./jmx_java_process_memory config +graph_category Java +... +root@re:/etc/munin/plugins# ./jmx_java_process_memory +java_memory_nonheap_committed.value 35291136 +... + +If you have configured environment for jmxurl, do not forget to export it before! + +-------- Configuration Files --------- + +Folder "examples" contains configuration files for Java and Tomcat monitoring examples. +The format of configuration file is a superset of Munin plugin "config" command output +(http://munin.projects.linpro.no/wiki/protocol-config) +It has the following additions: + +.jmxObjectName JMX object name, e.g. java.lang:type=Memory +.jmxAttributeName JMX attribute name, e.g. NonHeapMemoryUsage +.jmxAttributeKey If attribute is a composed data (structure), the name of the field in structure, e.g. max + +% separates comments in file + + + From bc344d005e731314506273cb4646f89dd72f8cec Mon Sep 17 00:00:00 2001 From: Matt Stith Date: Mon, 5 Mar 2012 23:11:27 -0500 Subject: [PATCH 115/123] Switch to Ruby and MineQuery to remove an extra plugin dependency Most servers have minequery installed already, no need to also have OnlinePlayers when this is what MineQuery was built for. --- plugins/minecraft/minecraft-users | 58 +++++++++++++++++-------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/plugins/minecraft/minecraft-users b/plugins/minecraft/minecraft-users index a1038962..7e4861f3 100755 --- a/plugins/minecraft/minecraft-users +++ b/plugins/minecraft/minecraft-users @@ -1,32 +1,36 @@ -#!/bin/sh +#!/usr/local/bin/ruby # Config: -# [minecraft_players -# playerfile /etc/minecraft/players.txt -# subtract true +# [minecraft_users] +# env.host awesomeserver.com +# env.port 25566 # -# playerfile - location of player list file, for example from the OnlineUsers -# plugin -# subtract - OnlineUsers has a header above the user list, set this to true -# to subtract 1 from the output to compensate -case $1 in - config) - cat <<'EOM' -graph_title Connected players -graph_vlabel players -players.label players -graph_info Number of players connected to Minecraft -graph_category Minecraft -EOM - exit 0;; -esac -echo -n "players.value " +require 'socket' -count=`wc -l ${playerfile} | cut -d' ' -f1` -if [ $subtract="true" ]; -then - echo -n "$(($count - 1))" -else - echo $count -fi +if ARGV[0] == 'config' + puts "graph_title Connected players" + puts "graph_vlabel players" + puts "players.label players" + puts "graph_info Number of players connected to Minecraft" + puts "graph_category Minecraft" + exit +end +host = ENV['host'] +host = 'localhost' unless host + +port = ENV['port'] +port = '25566' unless port + +socket = TCPSocket.new(host, port) +socket.puts "QUERY" + +response = socket.read +response = response.split("\n") + +server_port = response[0].split(" ", 2)[1].to_i +player_count = response[1].split(" ", 2)[1].to_i +max_players = response[2].split(" ", 2)[1].to_i +player_list = response[3].split(" ", 2)[1].chomp[1..-2] + +puts "players.value #{player_count}" \ No newline at end of file From 7b9893484c3127269f668bd467b0c7718a63d62d Mon Sep 17 00:00:00 2001 From: Andreas Thienemann Date: Wed, 7 Mar 2012 11:53:51 +0100 Subject: [PATCH 116/123] Add snmp__areca_ plugin I still had lying around. Needs a bit of cleanup but in the meantime it works to poll temperature, fan rpm and voltage from Areca RAID controllers. --- plugins/sensors/snmp__areca_ | 292 +++++++++++++++++++++++++++++++++++ 1 file changed, 292 insertions(+) create mode 100755 plugins/sensors/snmp__areca_ diff --git a/plugins/sensors/snmp__areca_ b/plugins/sensors/snmp__areca_ new file mode 100755 index 00000000..ffbd8568 --- /dev/null +++ b/plugins/sensors/snmp__areca_ @@ -0,0 +1,292 @@ +#!/usr/bin/python + +# Copyright (C) 2009-2012 Andreas Thienemann +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Library General Public License as published by +# the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# + +""" +=head1 NAME + +snmp__areca_ - Munin plugin to get temperature, voltage or fan speed values +from Areca network enabled RAID controllers via SNMP. + +=head1 APPLICABLE SYSTEMS + +All machines with a SNMP capable ARECA raid controller. + +=head1 CONFIGURATION + +Make sure your Areca controller is accessible via SNMP from the munin host: +snmpwalk -v 1 -c snmp_community ip.add.re.ss + +The plugin is a wildcard plugin and can thus be used to retrieve different +values depending on the name of the file. + +Linking it as snmp_10.8.1.230_areca_fan would retrieve the fan speed values from +the Areca controller at 10.8.1.230. + +Valid values are fan, temp and volt. + +Add the following to your /etc/munin/plugin-conf.d/snmp__areca file: + + [snmp_ip.add.re.ss_*] + community private + version 1 + +Then test the plugin by calling the following commands: + +munin-run snmp_10.8.1.230_areca_temp config +munin-run snmp_10.8.1.230_areca_temp + +Both commands should output sensible data without failing. + +=head1 INTERPRETATION + +The plugin shows the temperature in Celsius or the fanspeed in rotations per minute. + +=head1 MAGIC MARKERS + + #%# family=contrib + #%# capabilities= + +=head1 VERSION + +0.0.1 + +=head1 BUGS + +None known. If you know of any, please raise a ticket at https://trac.bawue.org/munin/wiki/areca__snmp_ + +=head1 AUTHOR + +Andreas Thienemann + +=head1 LICENSE + +GPLv2 + +=cut +""" + +import pprint +import time +import sys +import re +import os +from pysnmp import v1, v2c, role, asn1 + +request_conf = { + "volt" : { + "label" : "Voltages", + "vlabel" : "Volt", + "graph" : "--base 1000 --logarithmic", + "oid" : ".1.3.6.1.4.1.18928.1.2.2.1.8" + }, + "fan" : { + "label" : "Fans", + "vlabel" : "RPM", + "graph" : "--base 1000 -l 0", + "oid" : ".1.3.6.1.4.1.18928.1.2.2.1.9" + }, + "temp" : { + "label" : "Temperatures", + "vlabel" : "Celsius", + "graph" : "--base 1000 -l 0", + "oid" : ".1.3.6.1.4.1.18928.1.2.2.1.10" + } +} + +# Sanity check and parsing of the commandline +host = None +port = 161 +request = None +try: + match = re.search("^(?:|.*\/)snmp_([^_]+)_areca_(.+)$", sys.argv[0]) + host = match.group(1) + request = match.group(2) + match = re.search("^([^:]+):(\d+)$", host) + if match is not None: + host = match.group(1) + port = match.group(2) +except: + pass + +if host is None or request is None: + print "# Error: Incorrect filename. Cannot parse host or request." + sys.exit(1) + +# Parse env variables +if os.getenv("community") is not None: + community = os.getenv("community") +else: + community = "public" +if os.getenv("version") is not None: + version = os.getenv("version") +else: + version = "1" + + +def get_data(): + # Fetch the data + results = snmpwalk(request_conf[request]["oid"]) + + # parse data + vals = [] + for i in range(0, len(results)): + idx, res = results[i][0].split(request_conf[request]["oid"])[1].split(".")[1:], results[i][1] + if idx[1] == '1': + vals.append([]) + vals[int(idx[2])-1].append(res) + if idx[1] == '2': + vals[int(idx[2])-1].append(res) + if idx[1] == '3': + if request == "volt": + res = float(res)/1000 + vals[int(idx[2])-1].append(res) + + return vals + +def snmpwalk(root): + + # Create SNMP manager object + client = role.manager((host, port)) + + # Create a SNMP request&response objects from protocol version + # specific module. + try: + req = eval('v' + version).GETREQUEST() + nextReq = eval('v' + version).GETNEXTREQUEST() + rsp = eval('v' + version).GETRESPONSE() + + except (NameError, AttributeError): + print '# Unsupported SNMP protocol version: %s\n%s' % (version, usage) + sys.exit(-1) + + # Store tables headers + head_oids = [root] + + encoded_oids = map(asn1.OBJECTID().encode, head_oids) + + result = []; + + while 1: + + # Encode OIDs, encode SNMP request message and try to send + # it to SNMP agent and receive a response + (answer, src) = client.send_and_receive(req.encode(community=community, encoded_oids=encoded_oids)) + + # Decode SNMP response + rsp.decode(answer) + + # Make sure response matches request (request IDs, communities, etc) + if req != rsp: + raise 'Unmatched response: %s vs %s' % (str(req), str(rsp)) + + # Decode BER encoded Object IDs. + oids = map(lambda x: x[0], map(asn1.OBJECTID().decode, rsp['encoded_oids'])) + + # Decode BER encoded values associated with Object IDs. + vals = map(lambda x: x[0](), map(asn1.decode, rsp['encoded_vals'])) + + # Check for remote SNMP agent failure + if rsp['error_status']: + # SNMP agent reports 'no such name' when walk is over + if rsp['error_status'] == 2: + # Switch over to GETNEXT req on error + # XXX what if one of multiple vars fails? + if not (req is nextReq): + req = nextReq + continue + # One of the tables exceeded + for l in oids, vals, head_oids: + del l[rsp['error_index']-1] + else: + raise 'SNMP error #' + str(rsp['error_status']) + ' for OID #' + str(rsp['error_index']) + + # Exclude completed OIDs + while 1: + for idx in range(len(head_oids)): + if not asn1.OBJECTID(head_oids[idx]).isaprefix(oids[idx]): + # One of the tables exceeded + for l in oids, vals, head_oids: + del l[idx] + break + else: + break + + if not head_oids: + return result + + # Print out results + for (oid, val) in map(None, oids, vals): + result.append((oid, str(val))) + # print oid + ' ---> ' + str(val) + + # BER encode next SNMP Object IDs to query + encoded_oids = map(asn1.OBJECTID().encode, oids) + + # Update request object + req['request_id'] = req['request_id'] + 1 + + # Switch over GETNEXT PDU for if not done + if not (req is nextReq): + req = nextReq + + raise "error" + + +def print_config(): + print "graph_title " + request_conf[request]["label"] + print "graph_vlabel " + request_conf[request]["vlabel"] + print "graph_args " + request_conf[request]["graph"] + print "graph_category sensors" + print "host_name", host + for dataset in get_data(): + if request == "volt": + if dataset[1] == "Battery Status": + continue + else: + print request + dataset[0] + ".label", dataset[1] + ref_val = float(dataset[1].split()[-1][:-1]) + print request + dataset[0] + ".warning", str(ref_val * 0.95) + ":" + str(ref_val * 1.05) + print request + dataset[0] + ".critical", str(ref_val * 0.80) + ":" + str(ref_val * 1.20) + if request == "temp": + print request + dataset[0] + ".label", dataset[1] + if dataset[1].startswith("CPU"): + print request + dataset[0] + ".warning", 55 + print request + dataset[0] + ".critical", 60 + if dataset[1].startswith("System"): + print request + dataset[0] + ".warning", 40 + print request + dataset[0] + ".critical", 45 + if request == "fan": + print request + dataset[0] + ".label", dataset[1] + print request + dataset[0] + ".warning", 2400 + print request + dataset[0] + ".critical", 2000 + + sys.exit(0) + + +if "config" in sys.argv[1:]: + print_config() +elif "snmpconf" in sys.argv[1:]: + print "require 1.3.6.1.4.1.18928.1.2.2.1.8.1.1" + sys.exit(0) +else: + for dataset in get_data(): + # Filter Battery Status (255 == Not installed) + if request == "volt" and dataset[1] == "Battery Status": + continue + print request + dataset[0] + ".value", dataset[2] + sys.exit(0) From 97cf6d3235f8f596eec5269d1d330c3b4c06f79b Mon Sep 17 00:00:00 2001 From: Andreas Thienemann Date: Wed, 7 Mar 2012 11:55:49 +0100 Subject: [PATCH 117/123] Minor changes to the documentation. --- plugins/sensors/freeipmi_ | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/sensors/freeipmi_ b/plugins/sensors/freeipmi_ index ab6860f8..101fea4f 100755 --- a/plugins/sensors/freeipmi_ +++ b/plugins/sensors/freeipmi_ @@ -1,6 +1,6 @@ #!/usr/bin/python # -# Copyright (C) 2011 Andreas Thienemann +# Copyright (C) 2011,2012 Andreas Thienemann # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -40,11 +40,13 @@ likely no bmc to query. In certain cases however bmc-info will just seem to hang for quite some time. In this case, autodetection does not work because the smbios table has -incorrect information. One system know to experience this problem is the +incorrect information. One system known to experience this problem is the HP Proliant Microserver. Adding env.freeipmi_args "--no-probing --driver-type=KCS --driver-address=0xca2 --register-spacing=1" -to the munin plugin configuration will make the plugin work. +to the munin plugin configuration will make the plugin work. This is the +specific line for the HP Proliant Microserver mentioned above. Your mileage +may vary. Basic configuration for every system is that the plugin needs to be called as root. From 06c1e27d34478e07d7b936c09ca1bc4141dd0bc6 Mon Sep 17 00:00:00 2001 From: Andreas Thienemann Date: Wed, 7 Mar 2012 11:59:04 +0100 Subject: [PATCH 118/123] Simple bash fixes to make comparision clearer. --- plugins/cyrus/cyrus-imapd | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/plugins/cyrus/cyrus-imapd b/plugins/cyrus/cyrus-imapd index 5345caa0..d765af6e 100755 --- a/plugins/cyrus/cyrus-imapd +++ b/plugins/cyrus/cyrus-imapd @@ -1,8 +1,6 @@ #!/bin/sh # -# $Id: cyrus-imapd 18 2011-07-15 09:14:04Z ixs $ -# -# Copyright (C) 2009-2011 Andreas Thienemann +# Copyright (C) 2009 - 2012 Andreas Thienemann # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Library General Public License as published by @@ -51,7 +49,7 @@ It displays the following three datapoints: =head1 VERSION - $Revision: 18 $ + 0.0.20120307 =head1 BUGS @@ -71,7 +69,7 @@ GPLv2 CONFIGDIR=$(awk -F : '/^configdirectory:/ { gsub(/ /, "", $2); print $2 }' /etc/imapd.conf 2> /dev/null) PROCDIR="${CONFIGDIR}/proc" -if [ "$1" = "autoconf" ]; then +if [ "$1" == "autoconf" ]; then if [ "x${CONFIGDIR}x" != "xx" ] && [ -d ${PROCDIR} ]; then echo yes else @@ -81,14 +79,14 @@ if [ "$1" = "autoconf" ]; then fi # Check if we actually got some sensible data -if [ "x${CONFIGDIR}x" = "xx" ]; then +if [ "x${CONFIGDIR}x" == "xx" ]; then exit 1 fi # If run with the "config"-parameter, give out information on how the # graphs should look. -if [ "$1" = "config" ]; then +if [ "$1" == "config" ]; then echo 'graph_title Cyrus IMAPd Load' echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel connections' From 227e510310512a65920758dbd7c11cf0afb50489 Mon Sep 17 00:00:00 2001 From: Andreas Thienemann Date: Wed, 7 Mar 2012 11:59:43 +0100 Subject: [PATCH 119/123] updated (c) line --- plugins/sensors/snmp__areca_ | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/sensors/snmp__areca_ b/plugins/sensors/snmp__areca_ index ffbd8568..dbb41454 100755 --- a/plugins/sensors/snmp__areca_ +++ b/plugins/sensors/snmp__areca_ @@ -1,6 +1,6 @@ #!/usr/bin/python -# Copyright (C) 2009-2012 Andreas Thienemann +# Copyright (C) 2009 - 2012 Andreas Thienemann # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Library General Public License as published by From 3c6692940b99b344f1b2636e5bd24111d8bbca2e Mon Sep 17 00:00:00 2001 From: Andreas Thienemann Date: Wed, 7 Mar 2012 12:02:30 +0100 Subject: [PATCH 120/123] Added an check_munin script I had lying around. If your nagios isntallation uses munin as a passive datasource a check_dummy script is normally installed for the active checks. This script is a replacement and will be able to query your munin-node to get current values. This is great to manually run a check. --- tools/nagios/check_munin | 187 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 tools/nagios/check_munin diff --git a/tools/nagios/check_munin b/tools/nagios/check_munin new file mode 100644 index 00000000..23d7dc7d --- /dev/null +++ b/tools/nagios/check_munin @@ -0,0 +1,187 @@ +#!/usr/bin/python +# +# Copyright (C) 2009 Andreas Thienemann +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Library General Public License as published by +# the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# + +# +# Nagios script to query a munin host for plugin values +# +# Can be used as an active check instead of check_dummy +# + +import optparse +import socket +import pprint +import sys +import re + +parser = optparse.OptionParser("usage: %prog -H -M [-P ] -D [] []") +parser.add_option("-H", "--host", dest="host", type="string", + help="specify host to poll") +parser.add_option("-M", "--module", dest="module", type="string", + help="munin module to poll") +parser.add_option("-P", "--port", dest="port", default=4949, + type="int", help="port number to poll") +parser.add_option("-D", "--debug", action="store_true", dest="debug", default=False, + help="Debug output") + +(options, args) = parser.parse_args() + +HOST = options.host +PORT = options.port +MODULE = options.module +DEBUG = options.debug + +if HOST == None or MODULE == None: + parser.error("options -H and -M are required.") + +def compare(val, thresh): + # Compare value to warning and critical threshoulds + # Handle different threshold formats: max, :max, min:, min:max + + val = float(val) + + # max + match = re.match("^[:]?([-+]?[0-9]+)$", str(thresh)) + if match: + max = float(match.group(1)) + if val > max: + return 3 + + + # min + match = re.match("^([-+]?[0-9]+):$", str(thresh)) + if match: + min = float(match.group(1)) + if val < min: + return 2 + + # min:max + match = re.match("^([-+]?[0-9]+):([-+]?[0-9]+)$", str(thresh)) + if match: + min, max = float(match.group(1)), float(match.group(2)) + if val < min or val > max: + return 1 + + # okay + return 0 + +def output(l, cat, desc, ret): + if len(l[cat]) > 0: + print MODULE, desc + ";" + for line in l["critical"]: + print "CRITICAL: " + line + ";" + for line in l["warning"]: + print "WARNING: " + line + ";" + for line in l["ok"]: + print "OK: " + line + ";" + sys.exit(ret) + +try: + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.connect((HOST, PORT)) + conn = s.makefile('wb', 0) +except: + print "Couldn't connect to requested host" + sys.exit(3) + + +if conn.readline().startswith("# munin node at"): + conn.writelines("config" + MODULE + "\n") + order = [] + data = {} + while True: + line = conn.readline() + if DEBUG: + pprint.pprint(line) + # Last message, bail + if line == ".\n": + break + + label = "" + + key, val = line.split(" ", 1) + if key.find(".") is not -1: + label = key.split(".")[0] + if label not in data: + data[label] = { "warning" : "", "critical" : "", "value" : "" } + order.append(label) + # No thresholds passed on the command line + if len(args) == 2: + data[label]["warning"] = args[0] + data[label]["critical"] = args[1] + + # No thresholds passed on the command line, take the munin supplied ones + if len(args) < 2: + if key.endswith("warning"): + data[label]["warning"] = val[:-1] + if key.endswith("critical"): + data[label]["critical"] = val[:-1] + + if data[label]["warning"] == "" or data[label]["critical"] == "": + print "UNKNOWN - Couldn't retrieve thresholds, pass some on the command line" + sys.exit(3) + + + conn.writelines("fetch " + MODULE + "\n") + while True: + line = conn.readline() + # Last line, bail + if line == ".\n": + if DEBUG: + pprint.pprint(data) + break + + key, val = line.split(" ", 1) + label = key.split(".")[0] + if key.endswith("value"): + data[label]["value"] = val[:-1] + + conn.writelines("quit\n") + +else: + print "UNKNOWN - No munin node detected" + sys.exit(3) + +conn.close() +s.close() + +l = { "ok" : [], "warning" : [], "critical" : [] } +for entry in order: + # compare actual data: 3 max exceeded, 2 minimum underrun, 1 outside limit, 0 okay + for tresh in ["critical", "warning"]: + val = data[entry]["value"] + tval = data[entry][tresh] + tmp = "" + if compare(val, tval) == 3: + tmp = entry + ": " + val + " has exceeded the maximum threshold of " + tval + break + elif compare(val, tval) == 2: + tmp = entry + ": " + val + " has underrun the minimum threshold of " + tval + break + elif compare(val, tval) == 1: + tmp = entry + ": " + val + " is outside of range " + tval + break + + if tmp != "": + l[tresh].append(tmp) + else: + l["ok"].append(entry + ": " + val + " is okay") + + +output(l, "critical", "CRITICAL", 2) +output(l, "warning", "WARNING", 1) +output(l, "ok", "OK", 0) From 031d4e669f779e1c66f8573d1f370b797b16c62e Mon Sep 17 00:00:00 2001 From: Andreas Thienemann Date: Wed, 7 Mar 2012 12:09:04 +0100 Subject: [PATCH 121/123] Plugin to check fanspeed readings from a Thecus NAS server. --- plugins/sensors/snmp__thecus_fans | 104 ++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100755 plugins/sensors/snmp__thecus_fans diff --git a/plugins/sensors/snmp__thecus_fans b/plugins/sensors/snmp__thecus_fans new file mode 100755 index 00000000..ec43e277 --- /dev/null +++ b/plugins/sensors/snmp__thecus_fans @@ -0,0 +1,104 @@ +#!/usr/bin/perl -w +# -*- cperl -*- + +=head1 NAME + +snmp__thecus_fans - Munin plugin to retrive fanspeed readings from a Thecus +NAS device running SNMP. + +=head1 APPLICABLE SYSTEMS + +All Thecus NAS devices which have the third party NETSNMPD module installed. +This is available at http://www.fajo.de/main/thecus/modules/netsnmpd + +=head1 CONFIGURATION + +As a rule SNMP plugins need site specific configuration. The default +configuration (shown here) will only work on insecure sites/devices. + + [snmp_*] + env.version 2 + env.community public + +In general SNMP is not very secure at all unless you use SNMP version +3 which supports authentication and privacy (encryption). But in any +case the community string for your devices should not be "public". + +Please see 'perldoc Munin::Plugin::SNMP' for further configuration +information. + +=head1 INTERPRETATION + +The plugin reports the current fan speed readings as reported by the thecusIO +module. + +=head1 MIB INFORMATION + +Private MIB + +=head1 MAGIC MARKERS + + #%# family=snmpauto + #%# capabilities=snmpconf + +=head1 VERSION + + 0.0.20120307 + +=head1 BUGS + +None known. + +=head1 AUTHOR + +Copyright (C) 2011 - 2012 Andreas Thienemann + +Based on the snmp__uptime plugin as a template. + +=head1 LICENSE + +GPLv2 or (at your option) any later version. + +=cut + +use strict; +use Munin::Plugin::SNMP; +use vars qw($DEBUG); + +$DEBUG = $ENV{'MUNIN_DEBUG'}; + +my $response; + +if (defined $ARGV[0] and $ARGV[0] eq "snmpconf") { + print "index 1.3.6.1.4.1.14822.101.21.\n"; + print "require 1.3.6.1.4.1.14822.101.21.5200.1.1.0 [0-9]\n"; + print "require 1.3.6.1.4.1.14822.101.21.5200.1.2.0 [0-9]\n"; + exit 0; +} + +if (defined $ARGV[0] and $ARGV[0] eq "config") { + my ($host) = Munin::Plugin::SNMP->config_session(); + print "host_name $host\n" unless $host eq 'localhost'; + print "graph_title Thecus Fans +graph_args --base 1000 -l 0 +graph_vlabel RPM +graph_info This graph shows the RPMs of the fans as reported by the ThecusIO module. +graph_category sensors +fan1.label Fan 1 +fan1.info Thecus CPU Fan +fan2.label Fan 2 +fan2.info Thecus System Fan +"; + exit 0; +} + +my $session = Munin::Plugin::SNMP->session(-translate => + [ -timeticks => 0x0 ]); + +my $fan1 = $session->get_single ("1.3.6.1.4.1.14822.101.21.5200.1.1.0") || 'U'; +my $fan2 = $session->get_single ("1.3.6.1.4.1.14822.101.21.5200.1.2.0") || 'U'; + +#print "Retrived uptime is '$uptime'\n" if $DEBUG; + +print "fan1.value ", $fan1, "\n"; +print "fan2.value ", $fan2, "\n"; From c96bafa128e27fecffaebc99503650b9f765e823 Mon Sep 17 00:00:00 2001 From: Andreas Thienemann Date: Wed, 7 Mar 2012 12:21:41 +0100 Subject: [PATCH 122/123] New plugin to monitor a single raw value from a disk. Great to chart LOAD_CYCLE_COUNT or CURRENT_PENDING_SECTORS as they indicate disk lifetime... --- plugins/disk/smart_raw__ | 145 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100755 plugins/disk/smart_raw__ diff --git a/plugins/disk/smart_raw__ b/plugins/disk/smart_raw__ new file mode 100755 index 00000000..e466a7dd --- /dev/null +++ b/plugins/disk/smart_raw__ @@ -0,0 +1,145 @@ + #!/usr/bin/python +# +# Copyright (C) 2011 Andreas Thienemann +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +""" +=head1 NAME + +smart_raw__ - Munin plugin to retreive raw SMART values from a disk. + +=head1 APPLICABLE SYSTEMS + +All machines with a SMART capable disk and smartmontools installed. + +This plugin is very useful if you want to need to monitor a single raw S.M.A.R.T. +value reported by a disk. Load Cycle Counts or Pending Sectors come to mind as +these are a good indicator of problems with a disk. + +=head1 CONFIGURATION + +The plugin should be installed as smart_raw_sda_193 which means that the smart value +numbered 193 will be read from the disk sda. + + +Basic configuration for every system is that the plugin needs to be called as root +in order to execute smartmontools. + +Add the following to your /etc/munin/plugin-conf.d/smart_raw: + + [smart_raw_sda_193] + user root + +=head1 INTERPRETATION + +Smart RAW values are provided as is. You need to undertand what you are monitoring... + +=head1 MAGIC MARKERS + + #%# family=contrib + #%# capabilities= + +=head1 VERSION + +0.0.1 + +=head1 BUGS + +None known. + +=head1 AUTHOR + +Andreas Thienemann + +=head1 LICENSE + +GPLv3+ + +=cut +""" + +import subprocess +import sys +import os +import re +import pprint + +# We are a wildcard plugin, figure out what we are being called for +try: + disk = sys.argv[0].split('_')[2] + value = sys.argv[0].split('_')[3] +except IndexError: + sys.exit(1) + +def normalize_name(name): + name = re.sub("[^a-z0-9A-Z]","_", name) + return name + +# Code sniplet from Philipp Keller +# http://code.pui.ch/2007/02/19/set-timeout-for-a-shell-command-in-python/ +def timeout_command(command, timeout): + """call shell-command and either return its output or kill it + if it doesn't normally exit within timeout seconds and return None""" + import subprocess, datetime, os, time, signal + start = datetime.datetime.now() + process = subprocess.Popen(command.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) + while process.poll() is None: + time.sleep(0.1) + now = datetime.datetime.now() + if (now - start).seconds> timeout: + os.kill(process.pid, signal.SIGKILL) + os.waitpid(-1, os.WNOHANG) + return None + return process.stdout.read() + +def read_smart(): + """Read SMART attributes""" + out = timeout_command("/usr/sbin/smartctl -A -d ata /dev/%s" % (disk), 2) + smart_attribs = dict() + for line in out.split('\n')[7:-2]: # Cut away the header and the footer + line = line.split() + smart_attribs[line[0]] = { + 'name' : line[1], + 'label' : normalize_name(line[1]), + 'raw' : line [-1], + } + return smart_attribs + +def print_config(): + """Return configuration arguments for munin""" + attribs = read_smart() + print "graph_title S.M.A.R.T raw value %s for drive %s" % (attribs[value]['name'], disk) + print "graph_vlabel Raw value" + print "graph_info RAW smartctl value" + print "graph_category disk" + print "graph_args --base 1000 -l 0" + + print "%s.label %s" % (value, attribs[value]['label']) + sys.exit(0) + +def fetch(): + attribs = read_smart() + print "%s.value %s" % (value, attribs[value]['raw']) + sys.exit(0) + +if "config" in sys.argv[1:]: + print_config() +elif "autoconf" in sys.argv[1:]: + pass +elif "suggest" in sys.argv[1:]: + pass +else: + fetch() From dd4afac8691047dc6c2b011ad642be66bc1cfd0d Mon Sep 17 00:00:00 2001 From: Steve Schnepp Date: Wed, 7 Mar 2012 12:53:52 +0100 Subject: [PATCH 123/123] Move the screenshots next to plugins --- images/README.md | 2 ++ .../{ => ejabberd_scanlog}/ejabberd_scanlog | 0 .../ejabberd/ejabberd_scanlog}/ejabberd_scanlog.png | Bin plugins/network/{ => netstat_bsd_}/netstat_bsd_ | 0 .../network/netstat_bsd_}/netstat_bsd_.png | Bin plugins/system/{ => snmp__fn}/snmp__fn | 0 .../system/snmp__fn}/snmp__fn-cpu.png | Bin .../system/snmp__fn}/snmp__fn-memory.png | Bin .../system/snmp__fn}/snmp__fn-sessions.png | Bin .../system/snmp__fn}/snmp__fn-vpnsessions.png | Bin 10 files changed, 2 insertions(+) create mode 100644 images/README.md rename plugins/ejabberd/{ => ejabberd_scanlog}/ejabberd_scanlog (100%) rename {images/network => plugins/ejabberd/ejabberd_scanlog}/ejabberd_scanlog.png (100%) rename plugins/network/{ => netstat_bsd_}/netstat_bsd_ (100%) rename {images/network => plugins/network/netstat_bsd_}/netstat_bsd_.png (100%) rename plugins/system/{ => snmp__fn}/snmp__fn (100%) rename {images => plugins/system/snmp__fn}/snmp__fn-cpu.png (100%) rename {images => plugins/system/snmp__fn}/snmp__fn-memory.png (100%) rename {images => plugins/system/snmp__fn}/snmp__fn-sessions.png (100%) rename {images => plugins/system/snmp__fn}/snmp__fn-vpnsessions.png (100%) diff --git a/images/README.md b/images/README.md new file mode 100644 index 00000000..fedf12ba --- /dev/null +++ b/images/README.md @@ -0,0 +1,2 @@ +Please **don't** put screenshots of your plugins here. +Put them right next to your plugins. diff --git a/plugins/ejabberd/ejabberd_scanlog b/plugins/ejabberd/ejabberd_scanlog/ejabberd_scanlog similarity index 100% rename from plugins/ejabberd/ejabberd_scanlog rename to plugins/ejabberd/ejabberd_scanlog/ejabberd_scanlog diff --git a/images/network/ejabberd_scanlog.png b/plugins/ejabberd/ejabberd_scanlog/ejabberd_scanlog.png similarity index 100% rename from images/network/ejabberd_scanlog.png rename to plugins/ejabberd/ejabberd_scanlog/ejabberd_scanlog.png diff --git a/plugins/network/netstat_bsd_ b/plugins/network/netstat_bsd_/netstat_bsd_ similarity index 100% rename from plugins/network/netstat_bsd_ rename to plugins/network/netstat_bsd_/netstat_bsd_ diff --git a/images/network/netstat_bsd_.png b/plugins/network/netstat_bsd_/netstat_bsd_.png similarity index 100% rename from images/network/netstat_bsd_.png rename to plugins/network/netstat_bsd_/netstat_bsd_.png diff --git a/plugins/system/snmp__fn b/plugins/system/snmp__fn/snmp__fn similarity index 100% rename from plugins/system/snmp__fn rename to plugins/system/snmp__fn/snmp__fn diff --git a/images/snmp__fn-cpu.png b/plugins/system/snmp__fn/snmp__fn-cpu.png similarity index 100% rename from images/snmp__fn-cpu.png rename to plugins/system/snmp__fn/snmp__fn-cpu.png diff --git a/images/snmp__fn-memory.png b/plugins/system/snmp__fn/snmp__fn-memory.png similarity index 100% rename from images/snmp__fn-memory.png rename to plugins/system/snmp__fn/snmp__fn-memory.png diff --git a/images/snmp__fn-sessions.png b/plugins/system/snmp__fn/snmp__fn-sessions.png similarity index 100% rename from images/snmp__fn-sessions.png rename to plugins/system/snmp__fn/snmp__fn-sessions.png diff --git a/images/snmp__fn-vpnsessions.png b/plugins/system/snmp__fn/snmp__fn-vpnsessions.png similarity index 100% rename from images/snmp__fn-vpnsessions.png rename to plugins/system/snmp__fn/snmp__fn-vpnsessions.png