diff --git a/.flake8 b/.flake8 new file mode 100644 index 00000000..61d90815 --- /dev/null +++ b/.flake8 @@ -0,0 +1,2 @@ +[flake8] +max-line-length = 99 diff --git a/.gitignore b/.gitignore index a01ee289..20420e43 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ .*.swp +*~ + +__pycache__/ diff --git a/.travis.yml b/.travis.yml index 76144bd2..f9b78219 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,17 +1,24 @@ --- -language: perl +dist: trusty install: - sudo apt-get update - - sudo apt-get --no-install-recommends install devscripts python python3 ruby php5-cli gawk ksh pylint + - sudo apt-get --no-install-recommends install devscripts python python3 ruby php5-cli gawk ksh zsh pylint shellcheck - sudo apt-get --no-install-recommends install pkg-config libdb-dev libvirt-dev libexpat-dev # - Munin/Plugin.pm is in "munin-node" on precise - sudo apt-get --no-install-recommends install munin-node + - sudo apt-get --no-install-recommends install libsys-virt-perl + + # we use locally installed cpan modules instead of travis' builtin 'homebrew' perl support + # The homebrew variation does not work for us, since: + # * it is built without threading + # * the 'libvirt' plugin requires Sys::Virt, which is not available via cpan anymore - thus we need to install the Debian package 'libsys-virt-perl', which was built with threading + - cpanm --local-lib=~/perl5 local::lib && eval $(perl -I ~/perl5/lib/perl5/ -Mlocal::lib) # Modules used by test script - cpanm --notest Capture::Tiny - cpanm --notest File::Find - cpanm --notest Test::More - # + # Modules used by plugins - cpanm --notest Asterisk::AMI - cpanm --notest BerkeleyDB @@ -24,7 +31,6 @@ install: - cpanm --notest Device::SerialPort - cpanm --notest FCGI::Client - cpanm --notest File::ReadBackwards - - cpanm --notest File::Tail::Multi - cpanm --notest Graphics::ColorObject - cpanm --notest IPC::Run3 - cpanm --notest IPC::ShareLite @@ -43,18 +49,21 @@ install: - cpanm --notest POE::Quickie - cpanm --notest Proc::ProcessTable - cpanm --notest Redis - - cpanm --notest WWW::Mechanize::TreeBuilder + - cpanm --notest Switch - cpanm --notest Text::Iconv + - cpanm --notest WWW::Mechanize::TreeBuilder - cpanm --notest YAML - cpanm --notest XML::LibXML - cpanm --notest XML::Simple - cpanm --notest XML::Smart - cpanm --notest XML::Twig - cpanm --notest nvidia::ml - # - Sys::Virt version matching the test system's libvirt-dev - - cpanm --notest DANBERR/Sys-Virt-0.9.8.tar.gz + - cpanm --notest experimental # Modules used by plugins, but missing on cpan + # - File::Tail::Multi # - Sun::Solaris::Kstat # - VMware::VIRuntime # - MythTV -script: "PERL5LIB=$PERL5LIB:/usr/share/perl5 prove" + +script: + - prove diff --git a/README.md b/README.md index 46578d99..fa10af06 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,13 @@ This is the repository for all user contributed stuff **This is usually where you want to begin your journey.** -Here you'll find all the plugins coming from http://exchange.munin-monitoring.org/. -That web site is for the time being disabled, new updates are done here. +Here you find a plethora of plugins for the most diverse topics. Please take a look and +improve existing or propose new plugins. + +Please read the [hints for plugin contributions](./plugins#contributed-munin-plugins). + +See the [gallery](http://gallery.munin-monitoring.org/) for a browsable overview of these plugins. -If a dedicated website comes back alive, its plugin backend will be this git repo. # contrib/templates/ - 3rd-party templates diff --git a/plugins/network/2wire b/plugins/2wire/2wire similarity index 97% rename from plugins/network/2wire rename to plugins/2wire/2wire index 874e2133..7d87eff1 100755 --- a/plugins/network/2wire +++ b/plugins/2wire/2wire @@ -66,7 +66,7 @@ graph_args --base 1000 -l 0 #graph_vlabel number of sessions in (-) / out (+) graph_vlabel number of sessions graph_scale no -graph_category 2wire +graph_category network graph_total Total graph_printf %.0lf @@ -84,7 +84,7 @@ graph_title 2wire NAT session type graph_args --base 1000 -l 0 graph_vlabel number of sessions graph_scale no -graph_category 2wire +graph_category network graph_total Total graph_printf %.0lf @@ -99,7 +99,7 @@ graph_title 2wire session uptime graph_args --base 1000 -l 0 graph_vlabel Uptime in days graph_scale no -graph_category 2wire +graph_category network sessionUptime.label Uptime sessionUptime.draw AREA @@ -112,7 +112,7 @@ graph_title 2wire DSL output power graph_args --base 1000 graph_vlabel dBmV graph_scale no -graph_category 2wire +graph_category network powerDown.label Down #powerDown.graph no @@ -124,7 +124,7 @@ graph_title 2wire DSL details graph_args --base 1000 graph_vlabel dB graph_scale no -graph_category 2wire +graph_category network noiseM.label Noise margin attenuation.label Attenuation @@ -136,7 +136,7 @@ graph_title 2wire Errors Time graph_args --base 1000 graph_vlabel s graph_scale no -graph_category 2wire +graph_category network cumSecErrors.label Cumulative Seconds w/Errors cumSecSevErrors.label Cumulative Seconds w/Severe Errors @@ -147,7 +147,7 @@ graph_title 2wire Block Correction graph_args --base 1000 graph_vlabel blocks/s graph_scale no -graph_category 2wire +graph_category network correctedBlocks.label Corrected Blocks correctedBlocks.type DERIVE @@ -160,7 +160,7 @@ multigraph 2wire_dsl_traffic graph_title 2wire DSL traffic graph_args --base 1024 -l 0 graph_vlabel Bytes in (-) / out (+) per second -graph_category 2wire +graph_category network receive.label Bps receive.type DERIVE receive.min 0 @@ -176,7 +176,7 @@ multigraph 2wire_dsl_bandwidth_monthly graph_title 2wire DSL bandwidth usage (monthly) graph_args --base 1024 -l 0 -M graph_vlabel Bytes -graph_category 2wire +graph_category network down.label Down up.label Up @@ -192,7 +192,7 @@ multigraph 2wire_dsl_bandwidth_weekly graph_title 2wire DSL bandwidth usage (weekly) graph_args --base 1024 -l 0 -M graph_vlabel Bytes -graph_category 2wire +graph_category network down.label Down up.label Up @@ -208,7 +208,7 @@ multigraph 2wire_dsl_bandwidth_daily graph_title 2wire DSL bandwidth usage (daily) graph_args --base 1024 -l 0 -M graph_vlabel Bytes -graph_category 2wire +graph_category network down.label Down up.label Up diff --git a/plugins/README.md b/plugins/README.md new file mode 100644 index 00000000..6257568a --- /dev/null +++ b/plugins/README.md @@ -0,0 +1,54 @@ +# Contributed Munin Plugins + +This plethora of plugins covering various topics was contributed by many different users of [munin](http://munin-monitoring.org). + +See the [gallery](http://gallery.munin-monitoring.org/) for a browsable overview of these plugins. + + +## Purpose of this repository + +This repository of contributed plugin strives to achieve the following goals: + +* allow users to find interesting plugins +* allow contributors to publish their plugins +* simplify cooperative maintenance of plugins + +Contributed plugins are maintained primarily by their authors. +You may file bug reports for plugin issue here in this repository (`munin-contrib`), but please do not forget to notify the author the plugin, as well. + +Please note, that this repository is not supposed to be a dumping site for random plugins of low quality. The related infrastructure (e.g the [gallery](http://gallery.munin-monitoring.org/) or automated tests) require a certain level of quality. Please see below for details. + + +## Submit a new plugin + +1. check if a similar plugin exists and if it can be extended/changed instead of adding a new plugin + * please avoid code copies - they are a maintenance burden +2. add [documentation](http://guide.munin-monitoring.org/en/latest/develop/documenting.html#plugin-documentation) including configuration, author, license and [magic markers](http://guide.munin-monitoring.org/en/latest/architecture/syntax.html#magic-markers) +3. pick a suitable [category](http://guide.munin-monitoring.org/en/latest/reference/graph-category.html) +5. use style check tools for the language of the plugin (e.g. `shellcheck` for shell and `flake8` for Python) +6. pick a suitable [name and location](#Plugin_name_and_location) +7. bonus: + * use the [multigraph approach](http://guide.munin-monitoring.org/en/latest/plugin/multigraphing.html#plugin-multigraphing) for non-trivial plugins + * add [example graphs](http://munin-monitoring.org/wiki/PluginGallery#Rulesforplugincontributors) for the [gallery](http://gallery.munin-monitoring.org/) + * support [dirtyconfig](http://guide.munin-monitoring.org/en/latest/plugin/protocol-dirtyconfig.html#plugin-protocol-dirtyconfig) if it is suitable +8. open a [pull request](https://github.com/munin-monitoring/contrib/pull/) with your new plugin or send it attached to an email to the [mailing list](https://lists.sourceforge.net/lists/listinfo/munin-users) + +See the [plugin development documentation](http://guide.munin-monitoring.org/en/latest/develop/plugins/index.html) for more details. + + +## Modify an existing plugin + +* *try* to keep the plugin backwards compatible (e.g. keep data fieldnames unchanged) + * improvements of code quality and features can justify incompatible changes of existing plugins +* bonus: + * improve the existing plugins according to the [wishlist for new plugins](#Submit_a_new_plugin) + * upgrades from simple plugins to a [multigraph plugin](http://guide.munin-monitoring.org/en/latest/plugin/multigraphing.html#plugin-multigraphing) are welcome + + +## Plugin name and location + +The following descriptions are *intentions* - they do not necessarily describe the current state for all plugins. Please open a [pull request](https://github.com/munin-monitoring/contrib/pull/) if you want to align the current structure along the goals outlined below: + +* the top level directory should describe a related *software* or *vendor* + * use *concepts* or *platforms* only if it is really necessary (e.g. *cpu*, *bsd*, *memory*) +* subdirectories are usually not required diff --git a/plugins/network/accounting_ b/plugins/accounting/accounting_ old mode 100644 new mode 100755 similarity index 80% rename from plugins/network/accounting_ rename to plugins/accounting/accounting_ index 79a0635a..7628d3a2 --- a/plugins/network/accounting_ +++ b/plugins/accounting/accounting_ @@ -115,7 +115,7 @@ fi if [ "$1" == "autoconf" ]; then if [ -r /proc/net/dev ]; then - $IPTABLES -L INPUT -v -n -x >/dev/null 2>/dev/null + $IPTABLES -L INPUT -v -n -x -w >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "no (could not run iptables as user `whoami`)" exit 1 @@ -132,11 +132,11 @@ fi if [ "$1" = "suggest" ]; then if [ $PROTO = "ipv4" ]; then - $IPTABLES -L INPUT -v -x -n 2>/dev/null | sed -n 's/^.*\/\* ACCT\-\([a-zA-Z\-]*\) \*\/.*$/\ipv4_\1/p' - $IPTABLES -L OUTPUT -v -x -n 2>/dev/null | sed -n 's/^.*\/\* ACCT\-\([a-zA-Z\-]*\) \*\/.*$/\ipv4_\1/p' + $IPTABLES -L INPUT -v -x -n -w 2>/dev/null | sed -n 's/^.*\/\* ACCT\-\([a-zA-Z\-]*\) \*\/.*$/\ipv4_\1/p' + $IPTABLES -L OUTPUT -v -x -n -w 2>/dev/null | sed -n 's/^.*\/\* ACCT\-\([a-zA-Z\-]*\) \*\/.*$/\ipv4_\1/p' elif [ $PROTO == "ipv6" ]; then - $IPTABLES -L INPUT -v -x -n 2>/dev/null | sed -n 's/^.*\/\* ACCT\-\([a-zA-Z\-]*\) \*\/.*$/\ipv6_\1/p' - $IPTABLES -L OUTPUT -v -x -n 2>/dev/null | sed -n 's/^.*\/\* ACCT\-\([a-zA-Z\-]*\) \*\/.*$/\ipv6_\1/p' + $IPTABLES -L INPUT -v -x -n -w 2>/dev/null | sed -n 's/^.*\/\* ACCT\-\([a-zA-Z\-]*\) \*\/.*$/\ipv6_\1/p' + $IPTABLES -L OUTPUT -v -x -n -w 2>/dev/null | sed -n 's/^.*\/\* ACCT\-\([a-zA-Z\-]*\) \*\/.*$/\ipv6_\1/p' fi exit 0 @@ -150,7 +150,7 @@ if [ "$1" = "config" ]; then echo 'graph_args --base 1024 -l 0' echo 'graph_vlabel bytes per ${graph_period}' echo 'graph_order tcpIN udpIN icmpIN' - echo 'graph_category accounting' + echo 'graph_category network' echo 'tcpIN.label tcp received' echo 'tcpIN.cdef tcpIN,8,*' echo 'tcpIN.type DERIVE' @@ -172,7 +172,7 @@ if [ "$1" = "config" ]; then echo 'graph_args --base 1024 -l 0' echo 'graph_vlabel bytes per ${graph_period}' echo 'graph_order tcpOUT udpOUT icmpOUT' - echo 'graph_category accounting' + echo 'graph_category network' echo 'tcpOUT.label tcp sent' echo 'tcpOUT.cdef tcpOUT,8,*' echo 'tcpOUT.type DERIVE' @@ -192,12 +192,12 @@ if [ "$1" = "config" ]; then fi; echo 'multigraph '${0##*/}'_in' -$IPTABLES -L INPUT -v -n -x | grep "\/\* ACCT\-"$SUBCHAIN"\-tcp\-in \*\/" | tr -s '*' '-' | awk "{ print \"tcpIN.value \" \$2 }" -$IPTABLES -L INPUT -v -n -x | grep "\/\* ACCT\-"$SUBCHAIN"\-udp\-in \*\/" | tr -s '*' '-' | awk "{ print \"udpIN.value \" \$2 }" -$IPTABLES -L INPUT -v -n -x | grep "\/\* ACCT\-"$SUBCHAIN"\-icmp\-in \*\/" | tr -s '*' '-' | awk "{ print \"icmpIN.value \" \$2 }" +$IPTABLES -L INPUT -v -n -x -w | grep "\/\* ACCT\-"$SUBCHAIN"\-tcp\-in \*\/" | tr -s '*' '-' | awk "{ print \"tcpIN.value \" \$2 }" +$IPTABLES -L INPUT -v -n -x -w | grep "\/\* ACCT\-"$SUBCHAIN"\-udp\-in \*\/" | tr -s '*' '-' | awk "{ print \"udpIN.value \" \$2 }" +$IPTABLES -L INPUT -v -n -x -w | grep "\/\* ACCT\-"$SUBCHAIN"\-icmp\-in \*\/" | tr -s '*' '-' | awk "{ print \"icmpIN.value \" \$2 }" echo echo 'multigraph '${0##*/}'_out' -$IPTABLES -L OUTPUT -v -n -x | grep "\/\* ACCT\-"$SUBCHAIN"\-tcp\-out \*\/" | tr -s '*' '-' | awk "{ print \"tcpOUT.value \" \$2 }" -$IPTABLES -L OUTPUT -v -n -x | grep "\/\* ACCT\-"$SUBCHAIN"\-udp\-out \*\/" | tr -s '*' '-' | awk "{ print \"udpOUT.value \" \$2 }" -$IPTABLES -L OUTPUT -v -n -x | grep "\/\* ACCT\-"$SUBCHAIN"\-icmp\-out \*\/" | tr -s '*' '-' | awk "{ print \"icmpOUT.value \" \$2 }" +$IPTABLES -L OUTPUT -v -n -x -w | grep "\/\* ACCT\-"$SUBCHAIN"\-tcp\-out \*\/" | tr -s '*' '-' | awk "{ print \"tcpOUT.value \" \$2 }" +$IPTABLES -L OUTPUT -v -n -x -w | grep "\/\* ACCT\-"$SUBCHAIN"\-udp\-out \*\/" | tr -s '*' '-' | awk "{ print \"udpOUT.value \" \$2 }" +$IPTABLES -L OUTPUT -v -n -x -w | grep "\/\* ACCT\-"$SUBCHAIN"\-icmp\-out \*\/" | tr -s '*' '-' | awk "{ print \"icmpOUT.value \" \$2 }" diff --git a/plugins/mail/amavis-debian b/plugins/amavis/amavis-debian similarity index 94% rename from plugins/mail/amavis-debian rename to plugins/amavis/amavis-debian index 18ac8658..ef73e689 100755 --- a/plugins/mail/amavis-debian +++ b/plugins/amavis/amavis-debian @@ -18,7 +18,6 @@ # # Config variables: # AMAVIS_LOG - file where amavis logs are written -# STATEFILE - file which is needed to keep track of AMAVIS_LOG # LOGTAIL - location of logtail # BC - location of bc # @@ -27,7 +26,7 @@ # AMAVIS_LOG=${logfile:-/var/log/mail.log} -STATEFILE=/var/lib/munin/plugin-state/amavis.offset +STATEFILE=$MUNIN_PLUGSTATE/amavis.offset LOGTAIL=${logtail:-`which logtail`} BC=${bc:-`which bc`} @@ -47,7 +46,7 @@ fi if [ "$1" = "config" ]; then echo 'graph_title Amavis filter statistics' - echo 'graph_category postfix' + echo 'graph_category antivirus' echo 'graph_order total clean spam virus other' echo 'graph_vlabel Mails filtered' echo 'graph_scale no' diff --git a/plugins/mail/amavis_ b/plugins/amavis/amavis_ similarity index 98% rename from plugins/mail/amavis_ rename to plugins/amavis/amavis_ index ca14adba..c27d9cb2 100755 --- a/plugins/mail/amavis_ +++ b/plugins/amavis/amavis_ @@ -69,7 +69,7 @@ if ($ARGV[0] and $ARGV[0] eq "config") { if ($stats_type eq "cache") { print "graph_title Amavis cache hit / miss ratio\n"; print "graph_args --lower-limit 0 --upper-limit 100 --rigid\n"; - print "graph_category mail\n"; + print "graph_category antivirus\n"; print "graph_info The ratio of cache hits and misses for AMaViSd-new.\n"; print "graph_order hits misses\n"; print "graph_scale no\n"; @@ -84,7 +84,7 @@ if ($ARGV[0] and $ARGV[0] eq "config") { print "misses.min 0\n"; } elsif ($stats_type eq "content") { print "graph_title Amavis scanned mails\n"; - print "graph_category mail\n"; + print "graph_category antivirus\n"; print "graph_period minute\n"; print "graph_vlabel msgs / \${graph_period}\n"; foreach my $type (qw(total clean spam spammy virus)) { @@ -100,7 +100,7 @@ if ($ARGV[0] and $ARGV[0] eq "config") { } elsif ($stats_type eq "time") { print "graph_title Amavis average scan time\n"; print "graph_info Average time spent in each phase of the mail scanning process, per mail.\n"; - print "graph_category mail\n"; + print "graph_category antivirus\n"; print "graph_vlabel sec / mail\n"; print "graph_scale no\n"; diff --git a/plugins/mail/amavis_awk b/plugins/amavis/amavis_awk similarity index 96% rename from plugins/mail/amavis_awk rename to plugins/amavis/amavis_awk index ca361ec6..154a8d7c 100755 --- a/plugins/mail/amavis_awk +++ b/plugins/amavis/amavis_awk @@ -18,7 +18,7 @@ LOGDIR=${logdir:-/var/log/amavis} MAIL_LOG=$LOGDIR/${logfile:-amavisd.log} LOGTAIL=${logtail:-`which logtail`} -STATEFILE=/var/lib/munin/plugin-state/amavis.offset +STATEFILE=$MUNIN_PLUGSTATE/amavis.offset if [ "$1" = "autoconf" ]; then if [ -f "${MAIL_LOG}" -a -n "${LOGTAIL}" -a -x "${LOGTAIL}" ] ; then @@ -33,7 +33,7 @@ fi if [ "$1" = "config" ]; then echo 'graph_title Amavis message filtering' - echo 'graph_category mail' + echo 'graph_category antivirus' echo 'graph_vlabel Mails per minute' echo 'graph_args --base 1000 -l 0' diff --git a/plugins/amr/amr b/plugins/amr/amr index 7721e040..75b981b4 100755 --- a/plugins/amr/amr +++ b/plugins/amr/amr @@ -71,7 +71,7 @@ sub config print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Watt\n"; print "graph_total Total\n"; - print "graph_category AMR\n"; + print "graph_category sensors\n"; $count = 0; foreach my $station (sort keys %stations) { @@ -92,7 +92,7 @@ sub config print "graph_args --base 1000 -l 0\n"; print "graph_vlabel kWh\n"; print "graph_scale no\n"; - print "graph_category AMR\n"; + print "graph_category sensors\n"; $count = 0; foreach my $station (sort keys %stations) { @@ -106,7 +106,7 @@ sub config print "graph_title Known AMR stations\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel stations\n"; - print "graph_category AMR\n"; + print "graph_category sensors\n"; print "stations.label number of stations\n"; print "multigraph amr_signals\n"; @@ -114,7 +114,7 @@ sub config print "graph_args --base 1000 -l 0\n"; print "graph_vlabel signals / \${graph_period}\n"; print "graph_period minute\n"; - print "graph_category AMR\n"; + print "graph_category sensors\n"; $count = 0; foreach my $station (sort keys %stations) { my $name = clean_fieldname('station signals ' . $station); diff --git a/plugins/amule/amule_queue b/plugins/amule/amule_queue index 052b8903..649280bb 100755 --- a/plugins/amule/amule_queue +++ b/plugins/amule/amule_queue @@ -47,7 +47,7 @@ if [ "$1" = "config" ]; then echo 'graph_info Plugin available at http://linux.andreagozzi.com/content/munin_stuff.php' echo 'graph_args -l 0 --base 1000' echo 'graph_vlabel no of clients' - echo 'graph_category amule' + echo 'graph_category filetransfer' echo 'queue.label download clients' exit 0 fi; diff --git a/plugins/amule/amule_shares b/plugins/amule/amule_shares index 1fa7d92d..0b569cf0 100755 --- a/plugins/amule/amule_shares +++ b/plugins/amule/amule_shares @@ -47,7 +47,7 @@ if [ "$1" = "config" ]; then echo 'graph_info Plugin available at http://linux.andreagozzi.com/content/munin_stuff.php' echo 'graph_args -l 0 --base 1000' echo 'graph_vlabel no of shares' - echo 'graph_category amule' + echo 'graph_category filetransfer' echo 'shares.label shared files' echo 'shares.draw AREA' exit 0 diff --git a/plugins/amule/amule_transfers b/plugins/amule/amule_transfers index 4b243612..cae83ab0 100755 --- a/plugins/amule/amule_transfers +++ b/plugins/amule/amule_transfers @@ -53,7 +53,7 @@ if [ "$1" = "config" ]; then echo 'upload.max 50000' echo 'upload.min 0' echo 'graph_vlabel bytes per ${graph_period}' - echo 'graph_category amule' + echo 'graph_category filetransfer' echo 'upload.label upload speed' echo 'download.label download speed' exit 0 diff --git a/plugins/amule/amule_uptime b/plugins/amule/amule_uptime index 993a550e..9931a1ea 100755 --- a/plugins/amule/amule_uptime +++ b/plugins/amule/amule_uptime @@ -47,7 +47,7 @@ if [ "$1" = "config" ]; then echo 'graph_info Plugin available at http://linux.andreagozzi.com/content/munin_stuff.php' echo 'graph_args -l 0 --base 1000' echo 'graph_vlabel uptime in hours' - echo 'graph_category amule' + echo 'graph_category filetransfer' echo 'uptime.label uptime' echo 'uptime.draw AREA' exit 0 diff --git a/plugins/apache/apache_activity b/plugins/apache/apache_activity index ed6f22c4..99e0e966 100755 --- a/plugins/apache/apache_activity +++ b/plugins/apache/apache_activity @@ -65,7 +65,7 @@ if (exists $ARGV[0] and $ARGV[0] eq "autoconf") { if (exists $ARGV[0] and $ARGV[0] eq "config") { print "graph_title Apache activity\n"; print "graph_args --base 1000 -l 0\n"; - print "graph_category apache\n"; + print "graph_category webserver\n"; print "graph_vlabel processes\n"; foreach my $port (@PORTS) { while (my ($char, $val) = each (%chars)) { diff --git a/plugins/apache/apache_average_time_last_n_requests b/plugins/apache/apache_average_time_last_n_requests old mode 100644 new mode 100755 index f302eda4..1a32e602 --- a/plugins/apache/apache_average_time_last_n_requests +++ b/plugins/apache/apache_average_time_last_n_requests @@ -53,7 +53,7 @@ graph_title Apache average seconds last $LAST_N_REQUESTS requests graph_args --base 1000 graph_scale no graph_vlabel Average request time -graph_category Apache +graph_category webserver graph_info This graph shows average request times for the last $LAST_N_REQUESTS requests CONFIG ; diff --git a/plugins/apache/apache_blackbox b/plugins/apache/apache_blackbox index 372bfdaa..1346aa4e 100755 --- a/plugins/apache/apache_blackbox +++ b/plugins/apache/apache_blackbox @@ -6,7 +6,7 @@ # http://www.ziritione.org # # Installing: configure apache blackbox and set the logfile to /var/log/blackbox.log -# or change the BLACKBOXLOG setting bellow. +# or change the BLACKBOXLOG setting below. # # Dependencies: apache mod_logio, apache blackbox # http://www.devco.net/archives/2008/03/05/detailed_apache_stats.php @@ -64,7 +64,7 @@ sub print_config { print("graph_title HTTP requests status graph_args --base 1000 graph_vlabel requests / second -graph_category Network +graph_category webserver graph_total total\n"); for my $key (sort { $WANTED{$a} cmp $WANTED{$b} } (keys %WANTED)) { diff --git a/plugins/apache/apache_byprojects/README.md b/plugins/apache/apache_byprojects/README.md index 45fb8218..40ff2f29 100644 --- a/plugins/apache/apache_byprojects/README.md +++ b/plugins/apache/apache_byprojects/README.md @@ -16,9 +16,8 @@ Counts the in/out bandwidth used by each projects/vhost. [Logtail] (https://www. ## Installation The setup is pretty straight forward. First you need to configure the plugin: -Define the file which will be used by logtail to identify it's position in the log and the path to logtail: +Define the path to logtail: - $statepath = '/usr/local/var/munin/plugin-state'; # directory where logtail will save the state $logtail = '/usr/local/bin/logtail'; Multiple logs can be used for the same project/vhost and a regular expression (regex) can be used as a filter: diff --git a/plugins/apache/apache_byprojects/byprojects_access b/plugins/apache/apache_byprojects/byprojects_access old mode 100644 new mode 100755 index 5f917077..a6dc444b --- a/plugins/apache/apache_byprojects/byprojects_access +++ b/plugins/apache/apache_byprojects/byprojects_access @@ -26,7 +26,7 @@ use strict; my $server = 'Apache'; -my $statepath = '/usr/local/var/munin/plugin-state'; +my $statepath = $ENV{MUNIN_PLUGSTATE}; my $logtail = '/usr/local/bin/logtail'; my %logs = ( @@ -56,7 +56,7 @@ if(defined($ARGV[0])) { print "graph_title $server access byprojects\n"; print "graph_total Total\n"; print "graph_vlabel Access by \${graph_period}\n"; - print "graph_category $server\n"; + print "graph_category webserver\n"; print "graph_info This graph show $server access by various projects.\n"; while ((my $project, my @files) = each(%logs)) { print $project.".label $project\n"; diff --git a/plugins/apache/apache_byprojects/byprojects_bandwidth b/plugins/apache/apache_byprojects/byprojects_bandwidth old mode 100644 new mode 100755 index 8370cf79..5f6c5372 --- a/plugins/apache/apache_byprojects/byprojects_bandwidth +++ b/plugins/apache/apache_byprojects/byprojects_bandwidth @@ -31,7 +31,7 @@ use strict; my $server = 'Apache'; -my $statepath = '/usr/local/var/munin/plugin-state'; +my $statepath = $ENV{MUNIN_PLUGSTATE}; my $logtail = '/usr/local/bin/logtail'; my %logs = ( @@ -62,7 +62,7 @@ if(defined($ARGV[0])) { print "graph_title $server total bandwidth byprojects\n"; print "graph_total Total\n"; print "graph_vlabel Bits\n"; - print "graph_category $server\n"; + print "graph_category webserver\n"; print "graph_info This graph show $server total bandwidth used by various " . "projects.\n"; while ((my $project, my @files) = each(%logs)) { diff --git a/plugins/apache/apache_byprojects/byprojects_inout_bandwidth b/plugins/apache/apache_byprojects/byprojects_inout_bandwidth old mode 100644 new mode 100755 index 29dbecf9..5722ce52 --- a/plugins/apache/apache_byprojects/byprojects_inout_bandwidth +++ b/plugins/apache/apache_byprojects/byprojects_inout_bandwidth @@ -31,7 +31,7 @@ use strict; my $server = 'Apache'; -my $statepath = '/usr/local/var/munin/plugin-state'; +my $statepath = $ENV{MUNIN_PLUGSTATE}; my $logtail = '/usr/local/bin/logtail'; my %logs = ( @@ -59,7 +59,7 @@ if(defined($ARGV[0])) { print "graph_title $server in/out bandwidth byprojects\n"; print "graph_args --base 1000\n"; print "graph_vlabel bits per \${graph_period} in (-) / out (+)\n"; - print "graph_category $server\n"; + print "graph_category webserver\n"; print "graph_info This graph show $server in/out bandwidth used by various" . " projects.\n"; while ((my $project, my @files) = each(%logs)) { diff --git a/plugins/apache/apache_cache_disk_count b/plugins/apache/apache_cache_disk_count index e3f1c807..586df9d4 100755 --- a/plugins/apache/apache_cache_disk_count +++ b/plugins/apache/apache_cache_disk_count @@ -88,7 +88,7 @@ graph_title Apache mod_cache_disk usage graph_title Number of entries in mod_cache_disk Apache cache graph_args --base 1000 -l 0 graph_vlabel Y -graph_category Apache +graph_category webserver graph_order ${fieldnames[*]} total total.draw LINE1 total.label all diff --git a/plugins/apache/apache_memmory b/plugins/apache/apache_memmory index 7e1f3b97..10d89b3b 100755 --- a/plugins/apache/apache_memmory +++ b/plugins/apache/apache_memmory @@ -46,7 +46,7 @@ if [ "$1" = "config" ]; then echo 'graph_args --base 1000 -l 0 ' echo 'graph_vlabel Kb' echo 'graph_scale no' - echo 'graph_category apache' + echo 'graph_category webserver' echo 'graph_info Indicate the memdium size of all the apache child process.' diff --git a/plugins/apache/apache_servers b/plugins/apache/apache_servers index 93328de9..04238fdb 100755 --- a/plugins/apache/apache_servers +++ b/plugins/apache/apache_servers @@ -44,7 +44,7 @@ if [ "$1" = "config" ]; then echo 'graph_args --base 1000 -l 0 ' echo 'graph_vlabel servers' echo 'graph_scale no' - echo 'graph_category apache' + echo 'graph_category webserver' echo 'graph_info Indicate the number of apache servers running (child process).' diff --git a/plugins/apache/apache_smaps b/plugins/apache/apache_smaps index 1a855321..3f063c12 100755 --- a/plugins/apache/apache_smaps +++ b/plugins/apache/apache_smaps @@ -41,7 +41,7 @@ if (defined(@ARGV) && ($ARGV[0] eq 'config')) { print "graph_title Apache Smaps\n"; print "graph_args --base 1024 -l 0\n"; print "graph_vlabel Bytes\n"; - print "graph_category apache\n"; + print "graph_category webserver\n"; print "graph_info This graph shows memory usage for each given process.\n"; print "shr_max.label Shared memory max\n"; diff --git a/plugins/apache/apache_status b/plugins/apache/apache_status index c3a129a7..9ca3f043 100755 --- a/plugins/apache/apache_status +++ b/plugins/apache/apache_status @@ -171,7 +171,7 @@ multigraph apache_accesses graph_title Apache accesses graph_args --base 1000 -l 0 graph_vlabel accesses / \${graph_period} -graph_category apache +graph_category webserver END foreach my $port (@PORTS) { @@ -191,7 +191,7 @@ graph_title Apache processes graph_args --base 1000 -l 0 graph_vlabel processes graph_total total -graph_category apache +graph_category webserver END print "graph_order "; @@ -224,7 +224,7 @@ multigraph apache_volume graph_title Apache volume graph_args --base 1024 -l 0 graph_vlabel KiB per \${graph_period} -graph_category apache +graph_category webserver END foreach my $port (@PORTS) { diff --git a/plugins/apache/apache_threads b/plugins/apache/apache_threads index dc701bef..a231b392 100755 --- a/plugins/apache/apache_threads +++ b/plugins/apache/apache_threads @@ -42,7 +42,7 @@ if [ "$1" = "config" ]; then echo 'graph_args --base 1000 -l 0 ' echo 'graph_vlabel Threads' echo 'graph_scale no' - echo 'graph_category apache' + echo 'graph_category webserver' echo 'graph_info Indicate the memdium number of threads for all child process.' diff --git a/plugins/apache/apache_tmemmory b/plugins/apache/apache_tmemmory index c952a3ce..3617c753 100755 --- a/plugins/apache/apache_tmemmory +++ b/plugins/apache/apache_tmemmory @@ -45,7 +45,7 @@ if [ "$1" = "config" ]; then echo 'graph_args --base 1000 -l 0 ' echo 'graph_vlabel Mb' echo 'graph_scale no' - echo 'graph_category apache' + echo 'graph_category webserver' echo 'graph_info Indicate the total memory used by apache.' echo "servers.label servers" diff --git a/plugins/apache/apache_users b/plugins/apache/apache_users index 1c619f3f..4193a1af 100755 --- a/plugins/apache/apache_users +++ b/plugins/apache/apache_users @@ -12,7 +12,7 @@ # ####################################################################################### CONFIG -DIRECTORY=${MUNIN_PLUGINSTATE:-/var/lib/munin/plugin-state/apache_users} +DIRECTORY=$MUNIN_PLUGSTATE/apache_users TIMESTAMP=$DIRECTORY/.apache_users ACCESSLOG=/var/log/apache2/access_log @@ -45,7 +45,7 @@ REGEX=$(LC_ALL=en_US date -d "5 minutes ago" "+\[%d\/%b\/%Y:%H: %M :[0-5][0-9] % # Analyse logfile while read REQUEST_ADDR REQUEST_USERNAME REQUEST_BYTES do - # Name resolution for known adresses + # Name resolution for known addresses REQUEST_DOMAIN=$( awk '/^'"$REQUEST_ADDR"'/ { print $2 }' /etc/hosts ) REQUEST_ADDR=${REQUEST_DOMAIN:-$REQUEST_ADDR} @@ -133,7 +133,7 @@ then echo "graph_title Apache users" echo "graph_vlabel bytes per five minute period" echo "graph_args --lower-limit 1 --base 1024 --logarithmic" - echo "graph_category Apache" + echo "graph_category webserver" echo "graph_total total" echo "graph_info Webserver traffic by user." diff --git a/plugins/apache/apache_vhosts/apache_logparser b/plugins/apache/apache_vhosts/apache_logparser old mode 100644 new mode 100755 index 434ea0f9..1f427b5e --- a/plugins/apache/apache_vhosts/apache_logparser +++ b/plugins/apache/apache_vhosts/apache_logparser @@ -27,9 +27,6 @@ $statefile file to save last log position for tail $nsec tail and write to shared mem every n seconds $debug dump tallied data every n seconds, print every log line parsed $scan_interval rescan for new log files every n minutes -$type log file type: - common: CLF + vhost + time + (other fields) - combined: combined + time + (other fields) =cut # config @@ -38,14 +35,15 @@ my $files = "*access_log"; my $site = "(.*)-access_log"; my $statefile = "/tmp/logstate"; `touch $statefile` unless (-f $statefile); -local $type="combined"; local $nsec=7; local $debug=0; my $scan_interval=5; # minutes # perl modules -use File::Tail::Multi; +# "File:Tail:Multi" disappeared from CPAN (somewhen in 2016) - thus it needs +# to be imported carefully (for travis checks). +eval 'use File::Tail::Multi; 1;' or die 'Please install File::Tail::Multi'; use Storable qw(freeze thaw); use List::Util qw(min max); use IPC::ShareLite ':lock'; @@ -167,7 +165,7 @@ while (1) { $old{$vpm}{'max_bytes'}=max($old{$vpm}{'max_bytes'},$temp{$vpm}{'max_bytes'}) || 0; # reset local counters - foreach my $check qw(requests bytes time max_bytes avg_bytes max_time avg_time) { + foreach my $check (qw(requests bytes time max_bytes avg_bytes max_time avg_time)) { $temp{$vpm}{$check}=0; } diff --git a/plugins/apache/apache_vhosts/apache_pipelogger b/plugins/apache/apache_vhosts/apache_pipelogger old mode 100644 new mode 100755 index 09e39617..73c73eee --- a/plugins/apache/apache_vhosts/apache_pipelogger +++ b/plugins/apache/apache_vhosts/apache_pipelogger @@ -95,7 +95,7 @@ sub periodic_write { $old{$vpm}{'avg_bytes'}=sprintf("%d",($old{$vpm}{'avg_bytes'}+$temp{$vpm}{'avg_bytes'})/2); # reset local counters - foreach my $check qw(requests bytes time cml_time max_bytes avg_bytes max_time avg_time) { + foreach my $check (qw(requests bytes time cml_time max_bytes avg_bytes max_time avg_time)) { $temp{$vpm}{$check}=0; } diff --git a/plugins/apache/apache_vhosts/apache_vhosts b/plugins/apache/apache_vhosts/apache_vhosts old mode 100644 new mode 100755 index 9e249a3a..74150dce --- a/plugins/apache/apache_vhosts/apache_vhosts +++ b/plugins/apache/apache_vhosts/apache_vhosts @@ -117,7 +117,7 @@ multigraph apache_vhosts_$check graph_title average $check on all active vhosts graph_args --base 1000 graph_vlabel average $check per response -graph_category apache +graph_category webserver graph_period minute graph_order $order END @@ -139,7 +139,7 @@ multigraph apache_vhosts_$check.$site graph_title average $check on $data{$site}{'label'} graph_args --base 1000 graph_vlabel average response in $check -graph_category apache +graph_category webserver graph_period minute END @@ -168,7 +168,7 @@ multigraph apache_vhosts_requests graph_title requests by vhost graph_args --base 1000 graph_vlabel requests / \${graph_period} -graph_category apache +graph_category webserver graph_period minute graph_order $order END @@ -193,7 +193,7 @@ multigraph apache_vhosts_requests.$site graph_title status codes on $data{$site}{'label'} graph_args --base 1000 graph_vlabel status codes / \${graph_period} -graph_category apache +graph_category webserver graph_period minute END my $draw='AREA'; diff --git a/plugins/apache/apache_watch_ b/plugins/apache/apache_watch_ index c4e216c6..f0ffb101 100755 --- a/plugins/apache/apache_watch_ +++ b/plugins/apache/apache_watch_ @@ -91,7 +91,7 @@ my $action = $1; if (exists $ARGV[0] and $ARGV[0] eq "config") { print "graph_title Apache $plugs{$action}\n"; print "graph_args --base 1000 -l 0\n"; - print "graph_category apache\n"; + print "graph_category webserver\n"; print "graph_vlabel activity\n"; my $i = 0; foreach my $server (sort (@servers)) { diff --git a/plugins/apache/page_load b/plugins/apache/page_load old mode 100644 new mode 100755 index 19b169b5..cc09a885 --- a/plugins/apache/page_load +++ b/plugins/apache/page_load @@ -43,7 +43,7 @@ do_ () { do_config () { echo "graph_title Average page execution time" echo "graph_vlabel Seconds" - echo "graph_category apache" + echo "graph_category webserver" echo "graph_args --base 1000 -l 0" echo "graph_info Average page execution time" @@ -56,4 +56,4 @@ case $1 in eval do_$1 esac -exit $? \ No newline at end of file +exit $? diff --git a/plugins/qpid/qpid_bytedepth b/plugins/apache/qpid_bytedepth similarity index 97% rename from plugins/qpid/qpid_bytedepth rename to plugins/apache/qpid_bytedepth index 43959fe8..e328f9d8 100755 --- a/plugins/qpid/qpid_bytedepth +++ b/plugins/apache/qpid_bytedepth @@ -31,7 +31,7 @@ for q in queues: output_queue.append(re.sub('[^a-zA-Z0-9_]', '_', q.name)) if len(sys.argv) > 1 and sys.argv[1] == "config": - print "graph_category Qpid"; + print "graph_category webserver"; print "graph_title Queue byte depth"; print "graph_vlabel bytes" for queue in output_queue: diff --git a/plugins/qpid/qpid_discardsring b/plugins/apache/qpid_discardsring similarity index 97% rename from plugins/qpid/qpid_discardsring rename to plugins/apache/qpid_discardsring index 62d85d1b..99be6ef8 100755 --- a/plugins/qpid/qpid_discardsring +++ b/plugins/apache/qpid_discardsring @@ -31,7 +31,7 @@ for q in queues: output_queue.append(re.sub('[^a-zA-Z0-9_]', '_', q.name)) if len(sys.argv) > 1 and sys.argv[1] == "config": - print "graph_category Qpid"; + print "graph_category webserver"; print "graph_title Ring queue discard rate"; print "graph_vlabel messages/second"; for queue in output_queue: diff --git a/plugins/qpid/qpid_enqueuebytes b/plugins/apache/qpid_enqueuebytes similarity index 97% rename from plugins/qpid/qpid_enqueuebytes rename to plugins/apache/qpid_enqueuebytes index 8afc420f..ec31f053 100755 --- a/plugins/qpid/qpid_enqueuebytes +++ b/plugins/apache/qpid_enqueuebytes @@ -31,7 +31,7 @@ for q in queues: output_queue.append(re.sub('[^a-zA-Z0-9_]', '_', q.name)) if len(sys.argv) > 1 and sys.argv[1] == "config": - print "graph_category Qpid"; + print "graph_category webserver"; print "graph_title Enqueue data rate"; print "graph_vlabel bytes/second" for queue in output_queue: diff --git a/plugins/qpid/qpid_enqueuecount b/plugins/apache/qpid_enqueuecount similarity index 97% rename from plugins/qpid/qpid_enqueuecount rename to plugins/apache/qpid_enqueuecount index 45420d89..478ad13c 100755 --- a/plugins/qpid/qpid_enqueuecount +++ b/plugins/apache/qpid_enqueuecount @@ -31,7 +31,7 @@ for q in queues: output_queue.append(re.sub('[^a-zA-Z0-9_]', '_', q.name)) if len(sys.argv) > 1 and sys.argv[1] == "config": - print "graph_category Qpid"; + print "graph_category webserver"; print "graph_title Enqueue message rate"; print "graph_vlabel messages/second" for queue in output_queue: diff --git a/plugins/qpid/qpid_msgdepth b/plugins/apache/qpid_msgdepth similarity index 97% rename from plugins/qpid/qpid_msgdepth rename to plugins/apache/qpid_msgdepth index 33143d00..08013ebe 100755 --- a/plugins/qpid/qpid_msgdepth +++ b/plugins/apache/qpid_msgdepth @@ -31,7 +31,7 @@ for q in queues: output_queue.append(re.sub('[^a-zA-Z0-9_]', '_', q.name)) if len(sys.argv) > 1 and sys.argv[1] == "config": - print "graph_category Qpid"; + print "graph_category webserver"; print "graph_title Queue message depth"; print "graph_vlabel messages" for queue in output_queue: diff --git a/plugins/apt/approx b/plugins/apt/approx new file mode 100755 index 00000000..5a133a7e --- /dev/null +++ b/plugins/apt/approx @@ -0,0 +1,64 @@ +#!/usr/bin/env python +# +# vim:syntax=python +# +# Plugin to monitor the amount of packages in an approx cache. +# +# Usage: place in /etc/munin/plugins/ (or link it there using ln -s) +# +# Parameters understood: +# +# config (required) +# autoconf (optional - used by munin-config) +# +# Magic markers - optional - used by installation scripts and +# munin-config: +# +# #%# family=manual +# #%# capabilities=autoconf +# +# Now for the real work... + +from os.path import walk, exists, isfile, join +from sys import argv, exit + + +def get_file_types(): + """Returns an array of filetype => count.""" + out = {} + + def visitor(arg, dirname, names): + for filename in names: + if not isfile(join(dirname, filename)): + continue + ext = filename.split(".")[-1].lower() + out[ext] = out.get(ext, 0) + 1 + + walk('/var/cache/approx/', visitor, None) + return out + + +if len(argv) > 1: + + # Autoconfiguration + if argv[1] == "autoconf": + # Test if we can find a approx cache + if exists('/var/cache/approx'): + print("yes") + else: + print("no ('/var/cache/approx' not found)") + exit() + + elif argv[1] == "config": + print("graph_title Approx cache") + print("graph yes") + print("graph_category loadbalancer") + print("graph_info Statistics from the Approx cache.") + for filetype in get_file_types().keys(): + print("%s.label %s" % (filetype.lower(), filetype)) + exit() + +for filetype, count in get_file_types().iteritems(): + print("%s.value %d" % (filetype.lower(), count)) + +exit() diff --git a/plugins/apt/apt-proxy b/plugins/apt/apt-proxy index d8d204fb..48260f6d 100755 --- a/plugins/apt/apt-proxy +++ b/plugins/apt/apt-proxy @@ -36,7 +36,7 @@ if ($ARGV[0] and $ARGV[0] eq "config") { print "graph_title Files in apt-proxy cache\n"; print "graph_args --base 1000 -l 0\n"; - print "graph_category apt-proxy\n"; + print "graph_category security\n"; print "graph_vlabel files\n"; print "main.label main\n"; print "main.info Packages in main\n"; diff --git a/plugins/apt/deb_packages/.gitignore b/plugins/apt/deb_packages/.gitignore deleted file mode 100644 index f24cd995..00000000 --- a/plugins/apt/deb_packages/.gitignore +++ /dev/null @@ -1,27 +0,0 @@ -*.py[co] - -# Packages -*.egg -*.egg-info -dist -build -eggs -parts -bin -var -sdist -develop-eggs -.installed.cfg - -# Installer logs -pip-log.txt - -# Unit test / coverage reports -.coverage -.tox - -#Translations -*.mo - -#Mr Developer -.mr.developer.cfg diff --git a/plugins/apt/deb_packages/README.md b/plugins/apt/deb_packages/README.md index 56b60ff3..7dbfc489 100644 --- a/plugins/apt/deb_packages/README.md +++ b/plugins/apt/deb_packages/README.md @@ -22,7 +22,7 @@ version. This plugin has checked on Debian - Wheezy and squeeze. If you want to use it on older installations, tell me whether it works or which errors you had. It -shoud run past python-apt 0.7 and python 2.5. +should run past python-apt 0.7 and python 2.5. check out this git repository from @@ -30,14 +30,14 @@ check out this git repository from git clone git://github.com/munin-monitoring/contrib.git cd contrib/plugins/apt/deb_packages sudo cp deb_packages.py /etc/munin/plugins/deb_packages - sudo cp deb_packages.munin-conf /etc/munin/plugin-conf.d/deb_packages + sudo cp deb_packages.munin.conf /etc/munin/plugin-conf.d/deb_packages Verify the installation by sudo munin-run deb_packages ### Configuration -If you copied deb_packages.munin-conf to plugin-conf.d you have a starting point. +If you copied deb_packages.munin.conf to plugin-conf.d you have a starting point. A typical configuration looks like this [deb_packages] diff --git a/plugins/apt/deb_packages/deb_packages.munin-conf b/plugins/apt/deb_packages/deb_packages.munin.conf similarity index 100% rename from plugins/apt/deb_packages/deb_packages.munin-conf rename to plugins/apt/deb_packages/deb_packages.munin.conf diff --git a/plugins/apt/deb_packages/deb_packages.py b/plugins/apt/deb_packages/deb_packages.py index f0c37e11..538a8efc 100755 --- a/plugins/apt/deb_packages/deb_packages.py +++ b/plugins/apt/deb_packages/deb_packages.py @@ -24,7 +24,7 @@ TODO: update only if system was updated (aptitutde update has been run) TODO: shorten ext_info with getShortestConfigOfOptions TODO: check whether cachefile matches the config • i have no clever idea to do this without 100 lines of code -BUG: If a package will be upgraded, and brings in new dependancies, +BUG: If a package will be upgraded, and brings in new dependencies, these new deps will not be counted. WONTFIX """ import sys @@ -257,7 +257,7 @@ def getShortestConfigOfOptions(optionList = ['label', 'archive', 'site']): 'architecture' Architecture values are usually the same and can be ignored. - tells you wich representation of a tree as line is shortest. + tells you which representation of a tree as line is shortest. Is needed to say which ext.info line would be the shortest to write the shortest readable output. """ @@ -500,7 +500,7 @@ class PackageStat(defaultdict): "multigraph {graphName}_{type}\n"\ "graph_title {type} Debian packages sorted by {option}\n"\ "graph_info {type} Debian packages sorted by {option} of its repository\n"\ - "graph_category debian\n"\ + "graph_category security\n"\ "graph_vlabel packages".format(**d) def printConfig(self): @@ -855,3 +855,111 @@ if __name__=='__main__': muninPlugin = Munin() muninPlugin.execute() # import IPython; IPython.embed() + + +### The following is the smart_ plugin documentation, intended to be used with munindoc + +""" +=head1 NAME + +deb_packages - plugin to monitor update resources and pending packages on Debian + +=head1 APPLICABLE SYSTEMS + +This plugin has checked on Debian - Wheezy and squeeze. If you want to use it +on older installations, tell me whether it works or which errors you had. It +shoud run past python-apt 0.7 and python 2.5. + +=head1 DESCRIPTION + +With this plugin munin can give you a nice graph and some details where your +packages come from, how old or new your installation is. Furtermore it tells +you how many updates you should have been installed, how many packages are +outdated and where they come from. + +You can sort installed or upgradable Packages by 'archive', 'origin', 'site', +'label' and 'component' and even some of them at once. + +The script uses caching cause it is quite expensive. It saves the output to a +cachefile and checks on each run, if dpkg-status or downloaded Packagefile have +changed. If one of them has changed, it runs, if not it gives you the cached +version + +=head1 INSTALLATION + +check out this git repository from + +=over 2 + + aptitude install python-apt + git clone git://github.com/munin-monitoring/contrib.git + cd contrib/plugins/apt/deb_packages + sudo cp deb_packages.py /etc/munin/plugins/deb_packages + sudo cp deb_packages.munin.conf /etc/munin/plugin-conf.d/deb_packages + +=back + +Verify the installation by + +=over 2 + + sudo munin-run deb_packages + +=back + + +=head1 CONFIGURATION + +If you copied deb_packages.munin.conf to plugin-conf.d you have a starting point. + +A typical configuration looks like this + +=over 2 + + [deb_packages] + # plugin is quite expensive and has to write statistics to cache output + # so it has to write to plugins.cache + user munin + + # Packagelists to this size are printed as extra information to munin.extinfo + env.MAX_LIST_SIZE_EXT_INFO 50 + + # Age in seconds an $CACHE_FILE can be. If it is older, the script updates + # default if not set is 3540 (one hour) + # at the moment this is not used, the plugin always runs (if munin calls it) + # + env.CACHE_FILE_MAX_AGE 3540 + + # All these numbers are only for sorting, so you can use env.graph01_sort_by_0 + # and env.graph01_sort_by_2 without using env.graph01_sort_by_1. + # sort_by values ... + # possible values are 'label', 'archive', 'origin', 'site', 'component' + env.graph00_type installed + env.graph00_sort_by_0 label + env.graph00_sort_by_1 archive + env.graph00_show_ext_0 origin + env.graph00_show_ext_1 site + + env.graph01_type upgradable + env.graph01_sort_by_0 label + env.graph01_sort_by_1 archive + env.graph01_show_ext_0 origin + env.graph01_show_ext_1 site + +=back + +You can sort_by one or some of these possible Values + + +=head1 AUTHOR + +unknown + +=head1 LICENSE + +Default for Munin contributions is GPLv2 (http://www.gnu.org/licenses/gpl-2.0.txt) + +=cut + + +""" diff --git a/plugins/apt/deb_packages/example/packages_label_archive_upgradable-week.png b/plugins/apt/deb_packages/example-graphs/deb_packages.py-1.png similarity index 100% rename from plugins/apt/deb_packages/example/packages_label_archive_upgradable-week.png rename to plugins/apt/deb_packages/example-graphs/deb_packages.py-1.png diff --git a/plugins/apt/deb_packages/example-graphs/deb_packages.py-week.png b/plugins/apt/deb_packages/example-graphs/deb_packages.py-week.png new file mode 100644 index 00000000..4c2515d0 Binary files /dev/null and b/plugins/apt/deb_packages/example-graphs/deb_packages.py-week.png differ diff --git a/plugins/arangodb/arangodb_ b/plugins/arangodb/arangodb_ index 6d34c031..a90863af 100755 --- a/plugins/arangodb/arangodb_ +++ b/plugins/arangodb/arangodb_ @@ -91,14 +91,14 @@ def doConfig(plugin_name): print "graph_title ArangoDB current connections" print "graph_args --base 1000 -l 0" print "graph_vlabel connections" - print "graph_category ArangoDB" + print "graph_category db" print "connections.label connections" elif plugin_name == 'arangodb_time_total': print "graph_title ArangoDB total time" print "graph_args --base 1000 -l 0" print "graph_vlabel seconds" - print "graph_category ArangoDB" + print "graph_category db" print "total.label total" print "connection.label connection" print "request.label request" @@ -108,7 +108,7 @@ def doConfig(plugin_name): print "graph_title ArangoDB total bytes" print "graph_args --base 1024" print "graph_vlabel total bytes received (-) / sent (+)" - print "graph_category ArangoDB" + print "graph_category db" print "graph_order received sent" print "received.graph no" print "received.draw LINE2" diff --git a/plugins/aris/aris_players b/plugins/aris/aris_players index 93c9df74..b783f92c 100755 --- a/plugins/aris/aris_players +++ b/plugins/aris/aris_players @@ -31,13 +31,13 @@ $aris_db_pass='password'; if ($argv[1]=='config'){ print "graph_title ARIS active players\n"; print "graph_vlabel Players Count\n"; - print "graph_category ARIS\n"; + print "graph_category games\n"; print "players.label player count\n"; exit; } -$sqlLink = mysql_connect($aris_db_host,$aris_db_user,$aris_db_pass) or die('MySQL authenticaiton error'); +$sqlLink = mysql_connect($aris_db_host,$aris_db_user,$aris_db_pass) or die('MySQL authentication error'); mysql_select_db($aris_db_name) or die('MySQL Wrong Scheme Error'); $query = 'SELECT COUNT(DISTINCT player_id) AS count FROM player_log WHERE timestamp BETWEEN DATE_SUB(NOW(), INTERVAL 5 MINUTE) AND NOW()'; diff --git a/plugins/network/arp b/plugins/arp/arp similarity index 100% rename from plugins/network/arp rename to plugins/arp/arp diff --git a/plugins/network/arp_ b/plugins/arp/arp_ similarity index 100% rename from plugins/network/arp_ rename to plugins/arp/arp_ diff --git a/plugins/network/arp_bsd_ b/plugins/arp/arp_bsd_ old mode 100644 new mode 100755 similarity index 100% rename from plugins/network/arp_bsd_ rename to plugins/arp/arp_bsd_ diff --git a/plugins/other/assp-envelope-recipient-statistics b/plugins/assp/assp-envelope-recipient-statistics similarity index 99% rename from plugins/other/assp-envelope-recipient-statistics rename to plugins/assp/assp-envelope-recipient-statistics index 8f9f03cc..98366914 100755 --- a/plugins/other/assp-envelope-recipient-statistics +++ b/plugins/assp/assp-envelope-recipient-statistics @@ -129,7 +129,7 @@ sub response_error{ if( $ARGV[0] eq "config" ){ print "graph_title ASSP - Envelope Recipient Statistics\n"; print "graph_vlabel Counter in k,m\n"; - print "graph_category ASSP\n"; + print "graph_category spamfilter\n"; my $label; foreach my $key ( @muninlabel ){ diff --git a/plugins/other/assp-general-runtime-information b/plugins/assp/assp-general-runtime-information similarity index 99% rename from plugins/other/assp-general-runtime-information rename to plugins/assp/assp-general-runtime-information index c7aebc9d..931b0ed9 100755 --- a/plugins/other/assp-general-runtime-information +++ b/plugins/assp/assp-general-runtime-information @@ -113,7 +113,7 @@ if( $ARGV[0] eq "config" ){ if( $count == 0 ){ # General Runtime Information print "graph_title ASSP - General Runtime Information\n"; print "graph_vlabel Counter in Percent\n"; - print "graph_category ASSP\n"; + print "graph_category spamfilter\n"; } $label = $key; $label =~ s/\s/\_/g; diff --git a/plugins/other/assp-message-statistics b/plugins/assp/assp-message-statistics similarity index 99% rename from plugins/other/assp-message-statistics rename to plugins/assp/assp-message-statistics index e199fae7..0657dbd0 100755 --- a/plugins/other/assp-message-statistics +++ b/plugins/assp/assp-message-statistics @@ -141,7 +141,7 @@ sub response_error{ if( $ARGV[0] eq "config" ){ print "graph_title ASSP - Message Statistics\n"; print "graph_vlabel Counter in k,m\n"; - print "graph_category ASSP\n"; + print "graph_category spamfilter\n"; my $label; foreach my $key ( @muninlabel ){ diff --git a/plugins/other/assp-smtp-connection-statistics b/plugins/assp/assp-smtp-connection-statistics similarity index 99% rename from plugins/other/assp-smtp-connection-statistics rename to plugins/assp/assp-smtp-connection-statistics index cb9487b7..4caabb9f 100755 --- a/plugins/other/assp-smtp-connection-statistics +++ b/plugins/assp/assp-smtp-connection-statistics @@ -119,7 +119,7 @@ sub response_error{ if( $ARGV[0] eq "config" ){ print "graph_title ASSP - SMTP Connection Statistics\n"; print "graph_vlabel Counter in k,m\n"; - print "graph_category ASSP\n"; + print "graph_category spamfilter\n"; my $label; foreach my $key ( @muninlabel ){ diff --git a/plugins/other/assp-smtp-handler-statistics b/plugins/assp/assp-smtp-handler-statistics similarity index 99% rename from plugins/other/assp-smtp-handler-statistics rename to plugins/assp/assp-smtp-handler-statistics index 596e8469..c43570f6 100755 --- a/plugins/other/assp-smtp-handler-statistics +++ b/plugins/assp/assp-smtp-handler-statistics @@ -113,7 +113,7 @@ if( $ARGV[0] eq "config" ){ if( $count == 0 ){ # General Runtime Information print "graph_title ASSP - General Runtime Information\n"; print "graph_vlabel Counter in Percent\n"; - print "graph_category ASSP\n"; + print "graph_category spamfilter\n"; } $label = $key; $label =~ s/\s/\_/g; diff --git a/plugins/asterisk/asterisk b/plugins/asterisk/asterisk index 7a3c13ce..abd87f2c 100755 --- a/plugins/asterisk/asterisk +++ b/plugins/asterisk/asterisk @@ -85,8 +85,8 @@ sub asterisk_command { # Response: (Error|Follows|???) $line = $socket->getline; - if ($line ne "Response: Follows\r\n") { - while ( $line = $socket->getline and $line ne "\r\n" ) {} + if ($line !~ /^Response: Follows\r?\n$/) { + while ( $line = $socket->getline and $line !~ /^\r?\n$/ ) {} return undef; } @@ -94,12 +94,12 @@ sub asterisk_command { $line = $socket->getline; # Until we get the --END COMMAND-- marker, it's the command's output. - while ( $line = $socket->getline and $line ne "--END COMMAND--\r\n" ) { + while ( $line = $socket->getline and $line !~ /^--END COMMAND--\r?\n$/ ) { $reply .= $line; } # And then wait for the empty line that says we're done - while ( $line = $socket->getline and $line ne "\r\n" ) {} + while ( $line = $socket->getline and $line !~ /^\r?\n$/ ) {} return $reply; } @@ -130,13 +130,13 @@ if ( $socket ) { $socket->print("Action: login\nUsername: $username\nSecret: $secret\nEvents: off\n\n"); my $response_status = $socket->getline; - if ( $response_status ne "Response: Success\r\n" ) { + if ( $response_status !~ /^Response: Success\r?\n$/ ) { my $response_message = $socket->getline; - $response_message =~ s/Message: (.*)\r\n/$1/; + $response_message =~ s/Message: (.*)\r?\n/$1/; $error = "Asterisk authentication error: " . $response_message; } - while ( $line = $socket->getline and $line ne "\r\n" ) {} + while ( $line = $socket->getline and $line !~ /^\r?\n$/ ) {} } if ( $ARGV[0] and $ARGV[0] eq 'autoconf' ) { @@ -156,7 +156,7 @@ multigraph asterisk_channels graph_title Asterisk active channels graph_args --base 1000 -l 0 graph_vlabel channels -graph_category asterisk +graph_category voip total.label channels END @@ -173,8 +173,8 @@ multigraph asterisk_voicemail graph_title Asterisk voicemail messages graph_args --base 1000 -l 0 graph_vlabel messages -graph_category asterisk -messages.label Total messages +graph_category voip +total.label Total messages END print <close(); my $active_channels = 'U'; -$active_channels = $1 if $channels_response =~ /\n([0-9]+) active channels/; +$active_channels = $1 if $channels_response =~ /\n([0-9]+) active channels?/; print <disconnect; # Some idiot who wrote the SpanDSP code spelled "Negotiation" wrong. - print "t38_failed.value $faxstats{'Spandsp T.38'}{'Negotation Failed'}\n"; - print "g711_failed.value $faxstats{'Spandsp G.711'}{'Negotation Failed'}\n"; + print "t38_failed.value $faxstats{'Spandsp T.38'}{'Negotiation Failed'}\n"; + print "g711_failed.value $faxstats{'Spandsp G.711'}{'Negotiation Failed'}\n"; exit( 0 ); diff --git a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_nofax b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_nofax index 21d8a942..89db8c71 100755 --- a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_nofax +++ b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_nofax @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - No Faxes (T.38 and G.711)\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of No Faxes\n"; - print "graph_category asterisk\n"; + print "graph_category voip\n"; print "t38_nofax.draw AREA\n"; print "t38_nofax.label No T.38 Faxes\n"; print "g711_nofax.draw AREA\n"; diff --git a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_protocol_error b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_protocol_error index fe065bf8..316ca022 100755 --- a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_protocol_error +++ b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_protocol_error @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Protocol Errors (T.38 and G.711)\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of Protocol Errors\n"; - print "graph_category asterisk\n"; + print "graph_category voip\n"; print "t38_proterror.draw AREA\n"; print "t38_proterror.label T.38 Protocol Errors\n"; print "g711_proterror.draw AREA\n"; diff --git a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_retries_exceeded b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_retries_exceeded index dfb0d64c..b0f674f5 100755 --- a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_retries_exceeded +++ b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_retries_exceeded @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Retries Exceeded (T.38 and G.711)\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Retries Exceeded\n"; - print "graph_category asterisk\n"; + print "graph_category voip\n"; print "t38_retries.draw AREA\n"; print "t38_retries.label T.38 Retries Exceeded\n"; print "g711_retries.draw AREA\n"; diff --git a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_rxtx_protocol_error b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_rxtx_protocol_error index 3efed790..adf09b54 100755 --- a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_rxtx_protocol_error +++ b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_rxtx_protocol_error @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Rx and Tx Protocol Errors (T.38 and G.711)\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of Protocol Errors\n"; - print "graph_category asterisk\n"; + print "graph_category voip\n"; print "t38_rx_proterror.draw AREA\n"; print "t38_rx_proterror.label T.38 Rx Protocol Errors\n"; print "t38_tx_proterror.draw AREA\n"; diff --git a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_success b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_success index 677d0d72..18809fa2 100755 --- a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_success +++ b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_success @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Successful Faxes (T.38 and G.711)\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of Successful Faxes\n"; - print "graph_category asterisk\n"; + print "graph_category voip\n"; print "t38_success.draw AREA\n"; print "t38_success.label Successful T.38 Faxes\n"; print "g711_success.draw AREA\n"; diff --git a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_switched2t38 b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_switched2t38 index 8b029859..c236ecc5 100755 --- a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_switched2t38 +++ b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_switched2t38 @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Switched to T.38\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of Switches to T.38\n"; - print "graph_category asterisk\n"; + print "graph_category voip\n"; print "switched.draw AREA\n"; print "switched.label Switched to T.38\n"; exit 0; diff --git a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_train_failure b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_train_failure index b0852d68..0dfd6c98 100755 --- a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_train_failure +++ b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_train_failure @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Train Failures (T.38 and G.711)\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of Train Failures\n"; - print "graph_category asterisk\n"; + print "graph_category voip\n"; print "t38_failure.draw AREA\n"; print "t38_failure.label T.38 Train Failures\n"; print "g711_failure.draw AREA\n"; diff --git a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_txrx_attempts b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_txrx_attempts index 7f61b968..47e5f1a8 100755 --- a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_txrx_attempts +++ b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_txrx_attempts @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Tx/Rx Attempts\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of Tx and Rx Attempts\n"; - print "graph_category asterisk\n"; + print "graph_category voip\n"; print "transmit.draw AREA\n"; print "transmit.label Tx Attempts\n"; print "receive.draw AREA\n"; diff --git a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_unknown_error b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_unknown_error index 10b40354..724404d5 100755 --- a/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_unknown_error +++ b/plugins/asterisk/asterisk_18_fax_spandsp/asterisk_fax_unknown_error @@ -85,7 +85,7 @@ address with the copyright notice upgrade with your name. print "graph_title Asterisk Fax - Unknown Errors (T.38 and G.711)\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Number of Unknown Errors\n"; - print "graph_category asterisk\n"; + print "graph_category voip\n"; print "t38_errors.draw AREA\n"; print "t38_errors.label T.38 Unknown Errors\n"; print "g711_errors.draw AREA\n"; diff --git a/plugins/asterisk/asterisk_channels b/plugins/asterisk/asterisk_channels index a8bcaefd..a69c4af4 100755 --- a/plugins/asterisk/asterisk_channels +++ b/plugins/asterisk/asterisk_channels @@ -22,7 +22,7 @@ if [ "$1" = "config" ]; then echo "graph_title Asterisk Channels" echo "graph_args --base 1000 -l 0" echo "graph_vlabel Channels" - echo "graph_category asterisk" + echo "graph_category voip" echo "channels.label Channels" exit 0 fi diff --git a/plugins/asterisk/asterisk_inuse b/plugins/asterisk/asterisk_inuse index 549fe590..f782bf24 100755 --- a/plugins/asterisk/asterisk_inuse +++ b/plugins/asterisk/asterisk_inuse @@ -28,7 +28,7 @@ if [ "$1" = "config" ]; then echo 'graph_vlabel line use and connected users states' echo 'graph_noscale true' echo 'graph_info will be info' - echo 'graph_category asterisk' + echo 'graph_category voip' echo 'inuse.label inuse' echo 'inuse.type GAUGE' echo 'inuse.info sip show inuse' diff --git a/plugins/backup/backup.sh b/plugins/backup/backup.sh index 492a9fed..5ee33559 100755 --- a/plugins/backup/backup.sh +++ b/plugins/backup/backup.sh @@ -11,6 +11,7 @@ case $1 in cat <<'EOM' graph_title Number of young files at backup directory graph_vlabel number +graph_category backup autobackup.label number autobackup.critical 1: EOM @@ -18,5 +19,4 @@ EOM esac printf "autobackup.value " -find $BACKUP_DIR -mtime -$LIFETIME | wc -l - +find "$BACKUP_DIR" -mtime "-$LIFETIME" | wc -l diff --git a/plugins/backuppc/backuppc b/plugins/backuppc/backuppc index 222771e2..ea5f3cf8 100755 --- a/plugins/backuppc/backuppc +++ b/plugins/backuppc/backuppc @@ -28,7 +28,7 @@ if [ "$1" = "config" ]; then echo "graph_title BackupPC - Last Backup Size" echo "graph_args --base 1024 -l 0" echo "graph_vlabel Bytes" - echo "graph_category Backuppc" + echo "graph_category backup" for h in ${HOSTS} do @@ -40,26 +40,36 @@ if [ "$1" = "config" ]; then echo "graph_title BackupPC - Last Backup Age" echo "graph_args -l 0" echo "graph_vlabel days" - echo "graph_category Backuppc" + echo "graph_category backup" for h in ${HOSTS} do - echo "$(clean_fieldname ${h})_full.label $(clean_fieldname ${h}) Full" - echo "$(clean_fieldname ${h})_incr.label $(clean_fieldname ${h}) Incr" - if [ -n "$full_warning" ]; then - echo "$(clean_fieldname ${h})_full.warning $full_warning" - fi + echo "$(clean_fieldname ${h})_incr.label $(clean_fieldname ${h})" if [ -n "$incr_warning" ]; then echo "$(clean_fieldname ${h})_incr.warning $incr_warning" fi - if [ -n "$full_critical" ]; then - echo "$(clean_fieldname ${h})_full.critical $full_critical" - fi if [ -n "$incr_critical" ]; then echo "$(clean_fieldname ${h})_incr.critical $incr_critical" fi done + echo "multigraph backuppc_ages_full" + echo "graph_title BackupPC - Last Full Backup Age" + echo "graph_args -l 0" + echo "graph_vlabel days" + echo "graph_category backup" + + for h in ${HOSTS} + do + echo "$(clean_fieldname ${h})_full.label $(clean_fieldname ${h})" + if [ -n "$full_warning" ]; then + echo "$(clean_fieldname ${h})_full.warning $full_warning" + fi + if [ -n "$full_critical" ]; then + echo "$(clean_fieldname ${h})_full.critical $full_critical" + fi + done + exit 0 fi @@ -74,11 +84,16 @@ done echo "multigraph backuppc_ages" for h in $HOSTS +do + SIZE=$(awk '{ age = systime() - $3 } END { print age / 3600 / 24; }' ${PCDIR}/${h}/backups) + echo "$(clean_fieldname ${h})_incr.value $SIZE" +done + +echo "multigraph backuppc_ages_full" +for h in $HOSTS do SIZE=$(awk '/full/ { age = systime() - $3 } END { print age / 3600 / 24; }' ${PCDIR}/${h}/backups) echo "$(clean_fieldname ${h})_full.value $SIZE" - SIZE=$(awk '/incr/ { age = systime() - $3 } END { print age / 3600 / 24; }' ${PCDIR}/${h}/backups) - echo "$(clean_fieldname ${h})_incr.value $SIZE" done <<'__END__' diff --git a/plugins/bacula/bacula_job b/plugins/bacula/bacula_job index 2dffa984..6b8d7122 100755 --- a/plugins/bacula/bacula_job +++ b/plugins/bacula/bacula_job @@ -22,145 +22,149 @@ # # Parameters: # -# config (required) -# autoconf (optional - only used by munin-config) +# config (required) +# autoconf (optional - only used by munin-config) # # Magic markers (optional - only used by munin-config and some # installation scripts): # -#%# family=contrib -#%# capabilities=autoconf +# #%# family=contrib +# #%# capabilities=autoconf import subprocess -import time import sys import re import os + def parse_running_jobs(): - """ Parse the bconsole output once to get the running jobs """ + """ Parse the bconsole output once to get the running jobs """ - bconsole = subprocess.Popen("bconsole", stdin=subprocess.PIPE, stdout=subprocess.PIPE) - stdout, stderr = bconsole.communicate("status\n1\nstatus\n3\n.") + bconsole = subprocess.Popen("bconsole", stdin=subprocess.PIPE, stdout=subprocess.PIPE) + stdout, stderr = bconsole.communicate("status\n1\nstatus\n3\n.") - jobs = [] - clients = [] - clientlist = False + jobs = [] + clients = [] + clientlist = False - # Hold the line numbers for devices - dev_line = [] - input = stdout.split("\n") + # Hold the line numbers for devices + input_lines = stdout.split("\n") - for line, i in zip(input, range(0, len(input))): - if line.startswith("Connecting to Director "): - hostname = line.split()[-1].split(":")[0] + for line, i in zip(input_lines, range(0, len(input_lines))): + if line.startswith("Connecting to Director "): + hostname = line.split()[-1].split(":")[0] - if line.endswith(" is running"): - jobs.append(line.split()[2].split(".")[0]) + if line.endswith(" is running"): + jobs.append(line.split()[2].split(".")[0]) - # Parse the clientlist, warning, order of statements is important - if line.startswith("Select Client (File daemon) resource"): - clientlist = False + # Parse the clientlist, warning, order of statements is important + if line.startswith("Select Client (File daemon) resource"): + clientlist = False - if clientlist is True: - client_id, client_name = line.split() - client_clean = re.sub("^[^A-Za-z_]", "_", client_name, 1) - client_clean = re.sub("[^A-Za-z0-9_]", "_", client_clean, 0) - clients.append((client_name, client_clean, client_id[:-1])) + if clientlist is True: + client_id, client_name = line.split() + client_clean = re.sub("^[^A-Za-z_]", "_", client_name, 1) + client_clean = re.sub("[^A-Za-z0-9_]", "_", client_clean, 0) + clients.append((client_name, client_clean, client_id[:-1])) - if line.startswith("The defined Client resources are:"): - clientlist = True + if line.startswith("The defined Client resources are:"): + clientlist = True - return hostname, jobs, clients + return hostname, jobs, clients def parse(clients): - """ Parse the bconsole output """ + """ Parse the bconsole output """ - query_str = "" - for client in clients: - query_str = query_str + "status\n3\n" + client[1] + "\n" - query_str = query_str + "quit" + query_str = "" + for client in clients: + query_str = query_str + "status\n3\n" + client[1] + "\n" + query_str = query_str + "quit" - bconsole = subprocess.Popen("bconsole", stdin=subprocess.PIPE, stdout=subprocess.PIPE) - stdout, stderr = bconsole.communicate(query_str) + bconsole = subprocess.Popen("bconsole", stdin=subprocess.PIPE, stdout=subprocess.PIPE) + stdout, stderr = bconsole.communicate(query_str) - input = stdout.split("\n") + input_lines = stdout.split("\n") - jobstats = [] + jobstats = [] - for line, pos in zip(input, range(0, len(input))): + for line, pos in zip(input_lines, range(0, len(input_lines))): - # Get the client name - if line.startswith("Connecting to Client "): - # client_name = input[pos].split()[3].split(".")[0] - client_name = line.split()[3] - client_clean = re.sub("^[^A-Za-z_]", "_", client_name, 1) - client_clean = re.sub("[^A-Za-z0-9_]", "_", client_clean, 0) + # Get the client name + if line.startswith("Connecting to Client "): + # client_name = input_lines[pos].split()[3].split(".")[0] + client_name = line.split()[3] + client_clean = re.sub("^[^A-Za-z_]", "_", client_name, 1) + client_clean = re.sub("[^A-Za-z0-9_]", "_", client_clean, 0) - # Get the current bytes - if line.endswith(" is running."): - bytes = long(input[pos+2].split()[1].split("=")[1].replace(",", "")) - jobstats.append([client_name, client_clean, bytes]) + # Get the current bytes + if line.endswith(" is running."): + bytes_count_text = input_lines[pos+2].split()[1].split("=")[1].replace(",", "") + try: + # python2 + bytes_count = long(bytes_count_text) + except NameError: + # python3 + bytes_count = int(bytes_count_text) + jobstats.append([client_name, client_clean, bytes_count]) - job_dict = {} - for job in jobstats: - job_dict[job[0].split("-")[0]] = job + job_dict = {} + for job in jobstats: + job_dict[job[0].split("-")[0]] = job - return job_dict + return job_dict def print_config(): - hostname, jobs, clients = parse_running_jobs() - print "graph_title Bacula Job throughput" - print "graph_vlabel bytes per ${graph_period}" - print "graph_args --base 1024 -l 0" - print "graph_scale yes" - print "graph_info Bacula Job measurement." - print "graph_category Bacula" - print "graph_order", - for fd in clients: - print fd[1], - print - if os.getenv("report_hostname") is not None and \ - os.getenv("report_hostname").upper() in ["YES", "TRUE", "1", "Y"]: - print "host_name", hostname - for client in clients: - print "%s.label %s" % (client[1], client[0]) - print "%s.type DERIVE" % (client[1]) - print "%s.min 0" % (client[1]) -# print "%s.max %s" % (client[1], str(1024*1024*1024*16)) -# print "%s.cdef up,8,*" (client[1]) - sys.exit(0) + hostname, jobs, clients = parse_running_jobs() + print("graph_title Bacula Job throughput") + print("graph_vlabel bytes per ${graph_period}") + print("graph_args --base 1024 -l 0") + print("graph_scale yes") + print("graph_info Bacula Job measurement.") + print("graph_category backup") + print("graph_order", " ".join(fd[1] for fd in clients)) + print() + if ((os.getenv("report_hostname") is not None) and + (os.getenv("report_hostname").upper() in ["YES", "TRUE", "1", "Y"])): + print("host_name", hostname) + for client in clients: + print("%s.label %s" % (client[1], client[0])) + print("%s.type DERIVE" % (client[1])) + print("%s.min 0" % (client[1])) +# print("%s.max %s" % (client[1], str(1024*1024*1024*16))) +# print("%s.cdef up,8,*" (client[1])) + sys.exit(0) if "config" in sys.argv[1:]: - print_config() + print_config() elif "autoconf" in sys.argv[1:]: - for dir in os.getenv("PATH").split(":"): - for root, dirs, files in os.walk(dir): - if "bconsole" in files: - print "yes" - sys.exit(0) - print "no" - sys.exit(1) + for directory in os.getenv("PATH").split(":"): + for root, dirs, files in os.walk(directory): + if "bconsole" in files: + print("yes") + sys.exit(0) + print("no") + sys.exit(0) elif "suggest" in sys.argv[1:]: - sys.exit(1) + sys.exit(1) else: - hostname, jobs, clients = parse_running_jobs() - str = [] - for client in clients: - if client[0].split("-")[0] in jobs: - str.append((client[0], client[2])) + hostname, jobs, clients = parse_running_jobs() + client_pairs = [] + for client in clients: + if client[0].split("-")[0] in jobs: + client_pairs.append((client[0], client[2])) - client_values = parse(str) + client_values = parse(client_pairs) - for client in clients: - client_name_short = client[0].split("-")[0] - if client_name_short in client_values: - print "%s.value %s" % (client_values[client_name_short][1], client_values[client_name_short][2]) - else: - print "%s.value %s" % (client[1], "0") + for client in clients: + client_name_short = client[0].split("-")[0] + if client_name_short in client_values: + print("%s.value %s" % (client_values[client_name_short][1], + client_values[client_name_short][2])) + else: + print("%s.value %s" % (client[1], "0")) - sys.exit(0) + sys.exit(0) diff --git a/plugins/bacula/bacula_sd b/plugins/bacula/bacula_sd index 6ce537ae..b05ddae3 100755 --- a/plugins/bacula/bacula_sd +++ b/plugins/bacula/bacula_sd @@ -15,126 +15,129 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # - +# # # Munin Plugin to get storage device throughput for Bacula by parsing the bconsole # output. # # Parameters: # -# config (required) -# autoconf (optional - only used by munin-config) +# config (required) +# autoconf (optional - only used by munin-config) +# # - # Magic markers (optional - only used by munin-config and some # installation scripts): # -#%# family=contrib -#%# capabilities=autoconf +# #%# family=contrib +# #%# capabilities=autoconf import subprocess -import time import sys import re import os + def parse_devices(): - """ Parse the bconsole output once to get the device names """ + """ Parse the bconsole output once to get the device names """ - bconsole = subprocess.Popen("bconsole", stdin=subprocess.PIPE, stdout=subprocess.PIPE) - stdout, stderr = bconsole.communicate("status\n2") + bconsole = subprocess.Popen("bconsole", stdin=subprocess.PIPE, stdout=subprocess.PIPE) + stdout, stderr = bconsole.communicate("status\n2") - devs = [] + devs = [] - # Hold the line numbers for devices - dev_line = [] - input = stdout.split("\n") + # Hold the line numbers for devices + dev_line = [] + input_lines = stdout.split("\n") - for line, i in zip(input, range(0, len(input))): - if line.startswith("Connecting to Storage daemon "): - hostname = line.split()[-1].split(":")[0] - if line.startswith("Device \""): - dev_line.append(i) + for line, i in zip(input_lines, range(0, len(input_lines))): + if line.startswith("Connecting to Storage daemon "): + hostname = line.split()[-1].split(":")[0] + if line.startswith("Device \""): + dev_line.append(i) - for pos in dev_line: - # Get the device name - dev_name = input[pos].split()[1][1:-1] - dev_dev = input[pos].split()[2][1:-1] - dev_dev_clean = re.sub("^[^A-Za-z_]", "_", dev_dev, 1) - dev_dev_clean = re.sub("[^A-Za-z0-9_]", "_", dev_dev_clean, 0) - devs.append([dev_name, dev_dev, dev_dev_clean]) + for pos in dev_line: + # Get the device name + dev_name = input_lines[pos].split()[1][1:-1] + dev_dev = input_lines[pos].split()[2][1:-1] + dev_dev_clean = re.sub("^[^A-Za-z_]", "_", dev_dev, 1) + dev_dev_clean = re.sub("[^A-Za-z0-9_]", "_", dev_dev_clean, 0) + devs.append([dev_name, dev_dev, dev_dev_clean]) - return hostname, devs + return hostname, devs def parse(): - """ Parse the bconsole output """ + """ Parse the bconsole output """ - bconsole = subprocess.Popen("bconsole", stdin=subprocess.PIPE, stdout=subprocess.PIPE) - stdout, stderr = bconsole.communicate("status\n2") + bconsole = subprocess.Popen("bconsole", stdin=subprocess.PIPE, stdout=subprocess.PIPE) + stdout, stderr = bconsole.communicate("status\n2") - devstats = [] + devstats = [] - # Hold the line numbers for devices - dev_line = [] - input = stdout.split("\n") + # Hold the line numbers for devices + dev_line = [] + input_lines = stdout.split("\n") - for line, i in zip(input, range(0, len(input))): - if line.startswith("Device \""): - dev_line.append(i) + for line, i in zip(input_lines, range(0, len(input_lines))): + if line.startswith("Device \""): + dev_line.append(i) - for pos in dev_line: - # Get the device name - dev_dev = input[pos].split()[2][1:-1] - dev_dev_clean = re.sub("^[^A-Za-z_]", "_", dev_dev, 1) - dev_dev_clean = re.sub("[^A-Za-z0-9_]", "_", dev_dev_clean, 0) + for pos in dev_line: + # Get the device name + dev_dev = input_lines[pos].split()[2][1:-1] + dev_dev_clean = re.sub("^[^A-Za-z_]", "_", dev_dev, 1) + dev_dev_clean = re.sub("[^A-Za-z0-9_]", "_", dev_dev_clean, 0) - # Get the current bytes - if input[pos].endswith("is mounted with:"): - bytes = long(input[pos+5].split()[1].split("=")[1].replace(",", "")) - devstats.append([dev_dev, dev_dev_clean, bytes]) - else: - devstats.append([dev_dev, dev_dev_clean, 0]) + # Get the current bytes + if input_lines[pos].endswith("is mounted with:"): + bytes_count_text = input_lines[pos+5].split()[1].split("=")[1].replace(",", "") + try: + bytes_count = long(bytes_count_text) + except NameError: + bytes_count = int(bytes_count_text) + devstats.append([dev_dev, dev_dev_clean, bytes_count]) + else: + devstats.append([dev_dev, dev_dev_clean, 0]) - return devstats + return devstats def print_config(): - hostname, devstats = parse_devices() - print "graph_title Bacula Storage Daemon throughput" - print "graph_vlabel bytes per ${graph_period}" - print "graph_args --base 1024 -l 0" - print "graph_scale yes" - print "graph_info Bacula Storage Daemon througput measurement based on written bytes. This may be somewhat inacurate whenever a tape is changed." - print "graph_category Bacula" - print "graph_order", - for dev in devstats: - print dev[2], - print - if os.getenv("report_hostname") is not None and \ - os.getenv("report_hostname").upper() in ["YES", "TRUE", "1", "Y"]: - print "host_name", hostname - for dev in devstats: - print "%s.label %s" % (dev[2], dev[1]) - print "%s.type DERIVE" % (dev[2]) - print "%s.min 0" % (dev[2]) -# print "%s.max %s" % (dev[2], str(1024*1024*1024*16)) -# print "%s.cdef up,8,*" (dev[2]) - sys.exit(0) + hostname, devstats = parse_devices() + print("graph_title Bacula Storage Daemon throughput") + print("graph_vlabel bytes per ${graph_period}") + print("graph_args --base 1024 -l 0") + print("graph_scale yes") + print("graph_info Bacula Storage Daemon througput measurement based on written bytes. " + "This may be somewhat inacurate whenever a tape is changed.") + print("graph_category backup") + print("graph_order", " ".join([dev[2] for dev in devstats])) + print() + if (os.getenv("report_hostname") is not None + and (os.getenv("report_hostname").upper() in ["YES", "TRUE", "1", "Y"])): + print("host_name", hostname) + for dev in devstats: + print("%s.label %s" % (dev[2], dev[1])) + print("%s.type DERIVE" % (dev[2])) + print("%s.min 0" % (dev[2])) +# print("%s.max %s" % (dev[2], str(1024*1024*1024*16))) +# print("%s.cdef up,8,*" (dev[2])) + sys.exit(0) if "config" in sys.argv[1:]: - print_config() + print_config() elif "autoconf" in sys.argv[1:]: - for dir in os.getenv("PATH").split(":"): - for root, dirs, files in os.walk(dir): - if "bconsole" in files: - print "yes" - sys.exit(0) - print "no" - sys.exit(1) + for directory in os.getenv("PATH").split(":"): + for root, dirs, files in os.walk(directory): + if "bconsole" in files: + print("yes") + sys.exit(0) + print("no") + sys.exit(0) elif "suggest" in sys.argv[1:]: - sys.exit(1) + sys.exit(1) else: - for dev in parse(): - print "%s.value %s" % (dev[1], dev[2]) + for dev in parse(): + print("%s.value %s" % (dev[1], dev[2])) diff --git a/plugins/balanceng/bng b/plugins/balanceng/bng index 5515dbd9..16bd5726 100755 --- a/plugins/balanceng/bng +++ b/plugins/balanceng/bng @@ -201,7 +201,7 @@ graph_title Bng Throughput graph_order sent recv graph_args --base 1000 -l 0 graph_vlabel Packets/\${graph_period} -graph_category network +graph_category loadbalancer graph_info this graph shows the outbound traffic for $host sent.label Sent @@ -223,7 +223,7 @@ graph_title Interface $interface traffic graph_order sent recv graph_args --base 1000 -l 0 graph_vlabel Packets/\${graph_period} -graph_category network +graph_category loadbalancer graph_info this graph shows the total traffic for ${interface} sent.label ${interface}_Sent diff --git a/plugins/sensors/acpi-battery b/plugins/battery/acpi-battery similarity index 100% rename from plugins/sensors/acpi-battery rename to plugins/battery/acpi-battery diff --git a/plugins/sensors/power/acpi_batt_ b/plugins/battery/acpi_batt_ similarity index 100% rename from plugins/sensors/power/acpi_batt_ rename to plugins/battery/acpi_batt_ diff --git a/plugins/sensors/power/acpi_sys_batt_ b/plugins/battery/acpi_sys_batt_ similarity index 100% rename from plugins/sensors/power/acpi_sys_batt_ rename to plugins/battery/acpi_sys_batt_ diff --git a/plugins/sensors/power/batteries b/plugins/battery/batteries similarity index 99% rename from plugins/sensors/power/batteries rename to plugins/battery/batteries index 9df76c4e..197e9b7c 100755 --- a/plugins/sensors/power/batteries +++ b/plugins/battery/batteries @@ -268,7 +268,7 @@ if ($ARGV[0] and $ARGV[0] eq "config") $config{$graph}{'graph'}{'title'} = sprintf($graphs->{$graph}{'title'}, 'Mean batteryes'); $config{$graph}{'graph'}{'args'} = $graphs->{$graph}{'args'}; $config{$graph}{'graph'}{'vlabel'} = $graphs->{$graph}{'vlabel'}; - $config{$graph}{'graph'}{'category'} = 'power'; + $config{$graph}{'graph'}{'category'} = 'sensors'; foreach my $field (@{$graphs->{$graph}{'fields'}}) { if(($proc_data_exists and $fields->{$field}{'source'} eq 'proc') or $fields->{$field}{'source'} eq 'both') @@ -290,7 +290,7 @@ if ($ARGV[0] and $ARGV[0] eq "config") $config{$graph_name}{'graph'}{'info'} = sprintf("%s battery %s %s (sn: %s)", $info->{$i}{'battery_type'}, $info->{$i}{'manufacturer'}, $info->{$i}{'model_name'}, $info->{$i}{'serial_number'}); $config{$graph_name}{'graph'}{'args'} = '--base 1000'; $config{$graph_name}{'graph'}{'vlabel'} = $graphs->{$graph}{'vlabel'}; - $config{$graph_name}{'graph'}{'category'} = 'power'; + $config{$graph_name}{'graph'}{'category'} = 'sensors'; foreach my $field (@{$graphs->{$graph}{'fields'}}) { if(($proc_data_exists and $fields->{$field}{'source'} eq 'proc') or $fields->{$field}{'source'} eq 'both') diff --git a/plugins/sensors/battery_ b/plugins/battery/battery_ similarity index 98% rename from plugins/sensors/battery_ rename to plugins/battery/battery_ index 01f6f9d0..e91e20eb 100755 --- a/plugins/sensors/battery_ +++ b/plugins/battery/battery_ @@ -17,6 +17,7 @@ percent=${percent:-"no"} if [ "$1" = "config" ] then echo "graph_title Battery $battery_name" + echo "graph_category sensors" if [ "$percent" = "yes" ] then echo "graph_vlabel %" diff --git a/plugins/games/b3error_ b/plugins/bigbrother/b3error_ similarity index 97% rename from plugins/games/b3error_ rename to plugins/bigbrother/b3error_ index b2af28f6..23741fe2 100755 --- a/plugins/games/b3error_ +++ b/plugins/bigbrother/b3error_ @@ -19,7 +19,7 @@ B3NAME=${0##*/b3error_} LOGTAIL=${logtail:-`which logtail`} #logfile from env -STATEFILE=/var/lib/munin/plugin-state/${B3NAME}.offset +STATEFILE=$MUNIN_PLUGSTATE/${B3NAME}.offset mktempfile () { mktemp -t $1 @@ -92,4 +92,4 @@ echo "eventqueue.value ${eventqueue}" echo "parseline.value ${parseline}" echo "other.value ${other}" -exit 0 \ No newline at end of file +exit 0 diff --git a/plugins/network/dns/bind95_ b/plugins/bind/bind95_ similarity index 98% rename from plugins/network/dns/bind95_ rename to plugins/bind/bind95_ index 9883a9b9..bf42fe81 100755 --- a/plugins/network/dns/bind95_ +++ b/plugins/bind/bind95_ @@ -108,7 +108,7 @@ sub parseFile { if ( defined($ARGV[0]) && $ARGV[0] eq "config" ) { printf "graph_title Dns requests %s\n",($domain eq "View: _bind" ? " all domains":$domain); printf "graph_vlabel requests/s\n"; - printf "graph_category network\n"; + printf "graph_category dns\n"; printf "graph_info This graph display dns requests for %s\n",($domain eq "View: _bind" ? "all domains":$domain); foreach(@counters) { diff --git a/plugins/network/dns/bind9_resolver_stats b/plugins/bind/bind9_resolver_stats similarity index 97% rename from plugins/network/dns/bind9_resolver_stats rename to plugins/bind/bind9_resolver_stats index 86e410d5..80f20373 100755 --- a/plugins/network/dns/bind9_resolver_stats +++ b/plugins/bind/bind9_resolver_stats @@ -72,9 +72,7 @@ if [ "$1" = "config" ]; then # If dirty config capability is enabled then fall through # to output the data with the config information. - if [ "$MUNIN_CAP_DIRTYCONFIG" = "" ]; then - exit 0 - fi + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" != "1" ]; then exit 0; fi fi # Output the stats. diff --git a/plugins/bind9/bind9_rr b/plugins/bind/bind9_rr similarity index 100% rename from plugins/bind9/bind9_rr rename to plugins/bind/bind9_rr diff --git a/plugins/network/dns/bind9_server_stats b/plugins/bind/bind9_server_stats similarity index 90% rename from plugins/network/dns/bind9_server_stats rename to plugins/bind/bind9_server_stats index 4d495982..3c211e3c 100755 --- a/plugins/network/dns/bind9_server_stats +++ b/plugins/bind/bind9_server_stats @@ -69,11 +69,9 @@ if [ "$1" = "config" ]; then echo ${key}.type COUNTER done - # If dirty config capability is enabled then fall through - # to output the data with the config information. - if [ "$MUNIN_CAP_DIRTYCONFIG" = "" ]; then - exit 0 - fi + # If dirty config capability is enabled then fall through + # to output the data with the config information. + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" != "1" ]; then exit 0; fi fi # Output the stats. diff --git a/plugins/network/dns/bind9_socket_stats b/plugins/bind/bind9_socket_stats similarity index 89% rename from plugins/network/dns/bind9_socket_stats rename to plugins/bind/bind9_socket_stats index be742f33..f84261ed 100755 --- a/plugins/network/dns/bind9_socket_stats +++ b/plugins/bind/bind9_socket_stats @@ -67,11 +67,9 @@ if [ "$1" = "config" ]; then echo ${key}.type COUNTER done - # If dirty config capability is enabled then fall through - # to output the data with the config information. - if [ "$MUNIN_CAP_DIRTYCONFIG" = "" ]; then - exit 0 - fi + # If dirty config capability is enabled then fall through + # to output the data with the config information. + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" != "1" ]; then exit 0; fi fi # Output the stats. diff --git a/plugins/network/dns/bind_ b/plugins/bind/bind_ similarity index 98% rename from plugins/network/dns/bind_ rename to plugins/bind/bind_ index fb0a5ee5..92fd8759 100755 --- a/plugins/network/dns/bind_ +++ b/plugins/bind/bind_ @@ -72,7 +72,7 @@ sub parseFile { if ( defined($ARGV[0]) && $ARGV[0] eq "config" ) { printf "graph_title Dns requests %s\n",($domain eq "" ? " all domains":$domain); printf "graph_vlabel requests/s\n"; - printf "graph_category network\n"; + printf "graph_category dns\n"; printf "graph_info This graph display dns requests for %s\n",($domain eq "" ? " all domains":$domain); printf "success.label Success\n"; printf "success.type COUNTER\n"; diff --git a/plugins/network/bird b/plugins/bird/bird similarity index 99% rename from plugins/network/bird rename to plugins/bird/bird index 41d20273..b9c37926 100755 --- a/plugins/network/bird +++ b/plugins/bird/bird @@ -126,7 +126,7 @@ multigraph ${name}_routes graph_title $proto->{title} routes graph_args --base 1000 graph_vlabel routes -graph_category bird +graph_category network exported.label Exported routes exported.type GAUGE exported.info Exported routes @@ -146,7 +146,7 @@ multigraph ${name}_activity graph_title $proto->{title} activity graph_args --base 1000 graph_vlabel routes per second -graph_category bird +graph_category network import_updates_received.label Import updates received import_updates_received.type DERIVE import_updates_received.draw LINE1 diff --git a/plugins/boinc/boinc_credit b/plugins/boinc/boinc_credit index b21472bb..96cab8dd 100755 --- a/plugins/boinc/boinc_credit +++ b/plugins/boinc/boinc_credit @@ -205,7 +205,7 @@ if ( defined $ARGV[0] and $ARGV[0] eq 'config' ) { print <{id} <=> $b->{id} } @projdata ) { diff --git a/plugins/boinc/boinc_estwk b/plugins/boinc/boinc_estwk index f2634d00..0042f19e 100755 --- a/plugins/boinc/boinc_estwk +++ b/plugins/boinc/boinc_estwk @@ -101,7 +101,7 @@ if ( (defined $ARGV[0]) && ($ARGV[0] eq "config") ) { } print "graph_title BOINC work cache estimation\n"; - print "graph_category BOINC\n"; + print "graph_category htc\n"; print "graph_args --base 1000 -l 0 --alt-autoscale-max\n"; print "graph_vlabel Hours\n"; print "graph_scale no\n"; diff --git a/plugins/boinc/boinc_processes b/plugins/boinc/boinc_processes index 61854b57..adf6f584 100755 --- a/plugins/boinc/boinc_processes +++ b/plugins/boinc/boinc_processes @@ -98,7 +98,7 @@ sub config { while (chomp($reply = <$client>) && ($reply ne "")) { if ($reply =~ /(.*)<\/domain_name>/) { print "graph_title BOINC task progress [$1]\n"; - print "graph_category boinc\n"; + print "graph_category htc\n"; print "graph_args -l 0\n"; print "graph_vlabel %\n"; } diff --git a/plugins/boinc/boinc_projs b/plugins/boinc/boinc_projs index 37d1d713..25ee64b6 100755 --- a/plugins/boinc/boinc_projs +++ b/plugins/boinc/boinc_projs @@ -24,6 +24,10 @@ # # $Log$ # +# Revision 1.2 2016/10/04 Paul Saunders +# BoincCmd now translates states into words, so consider a "downloaded, scheduled, EXECUTING" task +# to be equivalent to a "2, 2, 1" task +# Really, this should be ported to use the proper RPC, rather than parsing boinccmd's output. # Revision 1.1 2011/03/22 Paul Saunders # Update for BOINC 6.12 # Add colours from http://boinc.netsoft-online.com/e107_plugins/forum/forum_viewtopic.php?3 @@ -145,7 +149,8 @@ for my $rslt_info (@rsltInfos) { my @acttask = grep /^\s+active_task_state: /,@lines; my $acttask = $acttask[0]; $acttask =~ s/^\s+active_task_state: //; - if ( ($schedstat eq "2") && ($state eq "2") && ($acttask eq "1") ) { + if (( ($schedstat eq "2") && ($state eq "2") && ($acttask eq "1") ) || + ( ($schedstat eq "scheduled") && ($state eq "downloaded") && ($acttask eq "EXECUTING") )) { # This is running task $projects{$url}->{prj_running} += 1; } @@ -223,7 +228,7 @@ if ( (defined $ARGV[0]) && ($ARGV[0] eq "config") ) { } print "graph_title Running BOINC processes\n"; - print "graph_category BOINC\n"; + print "graph_category htc\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel BOINC applications\n"; print "graph_total Total\n"; diff --git a/plugins/boinc/boinc_wus b/plugins/boinc/boinc_wus index 910690f8..3c9515cc 100755 --- a/plugins/boinc/boinc_wus +++ b/plugins/boinc/boinc_wus @@ -238,7 +238,7 @@ if ( (defined $ARGV[0]) && ($ARGV[0] eq "config") ) { } print "graph_title BOINC work status\n"; - print "graph_category BOINC\n"; + print "graph_category htc\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Workunits\n"; print "graph_total total\n"; diff --git a/plugins/snmp/snmp__brocade_temp_module_ b/plugins/brocade/snmp__brocade_temp_module_ similarity index 99% rename from plugins/snmp/snmp__brocade_temp_module_ rename to plugins/brocade/snmp__brocade_temp_module_ index 9f2954e2..074e5361 100755 --- a/plugins/snmp/snmp__brocade_temp_module_ +++ b/plugins/brocade/snmp__brocade_temp_module_ @@ -68,7 +68,7 @@ if ($ARGV[0] and $ARGV[0] eq "config") { print "graph_title Module $module graph_args --base 1000 --lower-limit 0 graph_vlabel °C -graph_category system +graph_category sensors graph_scale no\n"; my $descr = undef; diff --git a/plugins/disk/df_abs_bsd b/plugins/bsd/df_abs_bsd similarity index 100% rename from plugins/disk/df_abs_bsd rename to plugins/bsd/df_abs_bsd diff --git a/plugins/disk/df_bsd b/plugins/bsd/df_bsd similarity index 100% rename from plugins/disk/df_bsd rename to plugins/bsd/df_bsd diff --git a/plugins/network/netstat_bsd_m_/netstat_bsd_m_.png b/plugins/bsd/example-graphs/netstat_bsd_m_-day.png similarity index 100% rename from plugins/network/netstat_bsd_m_/netstat_bsd_m_.png rename to plugins/bsd/example-graphs/netstat_bsd_m_-day.png diff --git a/plugins/disk/freebsd_hdd_power_state b/plugins/bsd/freebsd_hdd_power_state similarity index 99% rename from plugins/disk/freebsd_hdd_power_state rename to plugins/bsd/freebsd_hdd_power_state index 538846a4..0ef69c69 100755 --- a/plugins/disk/freebsd_hdd_power_state +++ b/plugins/bsd/freebsd_hdd_power_state @@ -50,7 +50,7 @@ graph_title HDD power state graph_vlabel Power state graph_args --upper-limit 1 -l 0 graph_scale no -graph_category disk +graph_category sensors graph_info The power state of the hard disk drives on the system (1 is spun up, 0 is idle/standby). EOM echo $DEVICES | awk 'BEGIN{OFS=""} { for (i = 0; i++ < NF;) print "power", i, ".label ", $i }' diff --git a/plugins/network/netstat_bsd_m_/netstat_bsd_m_ b/plugins/bsd/netstat_bsd_m_ similarity index 100% rename from plugins/network/netstat_bsd_m_/netstat_bsd_m_ rename to plugins/bsd/netstat_bsd_m_ diff --git a/plugins/system/openbsd-memory b/plugins/bsd/openbsd-memory similarity index 100% rename from plugins/system/openbsd-memory rename to plugins/bsd/openbsd-memory diff --git a/plugins/system/openbsd-swap b/plugins/bsd/openbsd-swap similarity index 100% rename from plugins/system/openbsd-swap rename to plugins/bsd/openbsd-swap diff --git a/plugins/mail/spamd-blacklist-bsd b/plugins/bsd/spamd-blacklist-bsd similarity index 100% rename from plugins/mail/spamd-blacklist-bsd rename to plugins/bsd/spamd-blacklist-bsd diff --git a/plugins/mail/spamd-tarpit-bsd b/plugins/bsd/spamd-tarpit-bsd similarity index 100% rename from plugins/mail/spamd-tarpit-bsd rename to plugins/bsd/spamd-tarpit-bsd diff --git a/plugins/system/uptime_bsd b/plugins/bsd/uptime_bsd similarity index 100% rename from plugins/system/uptime_bsd rename to plugins/bsd/uptime_bsd diff --git a/plugins/cacti/cacti-host b/plugins/cacti/cacti-host index 2a90638f..47dd464e 100755 --- a/plugins/cacti/cacti-host +++ b/plugins/cacti/cacti-host @@ -21,7 +21,7 @@ if [[ $1 != "" ]]; then echo "graph_title Servidores consultados" echo "graph_vlabel Numero servidores (s)" echo "poller_hosts.label hosts(s)" - echo 'graph_category cacti' + echo 'graph_category munin' exit 0 fi fi diff --git a/plugins/cacti/cacti_poller_time b/plugins/cacti/cacti_poller_time index 8c6cc8d0..f843ea04 100755 --- a/plugins/cacti/cacti_poller_time +++ b/plugins/cacti/cacti_poller_time @@ -21,7 +21,7 @@ if [[ $1 != "" ]]; then echo "graph_title Poller Time" echo "graph_vlabel Time(s)" echo "poller_time.label time(s)" - echo 'graph_category cacti' + echo 'graph_category munin' exit 0 fi fi diff --git a/plugins/cacti/cacti_rrds b/plugins/cacti/cacti_rrds index d8aba858..efa83616 100755 --- a/plugins/cacti/cacti_rrds +++ b/plugins/cacti/cacti_rrds @@ -21,7 +21,7 @@ if [[ $1 != "" ]]; then echo "graph_title RRDs Procesados Time" echo "graph_vlabel Numero de RRDs" echo "rrds.label rrds" - echo 'graph_category cacti' + echo 'graph_category munin' exit 0 fi fi diff --git a/plugins/celery/celery_tasks b/plugins/celery/celery_tasks index d70e7994..ee65d17f 100755 --- a/plugins/celery/celery_tasks +++ b/plugins/celery/celery_tasks @@ -97,7 +97,7 @@ def print_config(task_names): print 'graph_args --lower-limit 0' print 'graph_scale no' print 'graph_vlabel tasks per ${graph_period}' - print 'graph_category celery' + print 'graph_category cloud' for name in task_names: print '%s.label %s' % (clean_task_name(name), name) diff --git a/plugins/celery/celery_tasks_states b/plugins/celery/celery_tasks_states index 0198d7ef..e62ab1c4 100755 --- a/plugins/celery/celery_tasks_states +++ b/plugins/celery/celery_tasks_states @@ -103,7 +103,7 @@ def print_config(workers = None): print 'graph_args --lower-limit 0' print 'graph_scale no' print 'graph_vlabel tasks per ${graph_period}' - print 'graph_category celery' + print 'graph_category cloud' for name in TASK_STATES: name = clean_state_name(name) diff --git a/plugins/ceph/ceph-osd-info b/plugins/ceph/ceph-osd-info index 0018d393..96933ce2 100755 --- a/plugins/ceph/ceph-osd-info +++ b/plugins/ceph/ceph-osd-info @@ -60,7 +60,7 @@ We don't bite. import socket,os,json,sys,re,glob,itertools -# this overides config values for specific graphs: +# this overrides config values for specific graphs: # "graphname.osd1":{"graph_title":"This is the graph for OSD 1!"} # "graphname.osd*":{"graph_title":"This is one of the 'dig deeper' graphs!"} # "graphname":{"graph_title":"This is my great graph!"} @@ -148,7 +148,7 @@ if (sys.argv.__len__()>1) and (sys.argv[1]=="config"): gr_simple=graph.replace("-","_").replace(":","_") gr_pretty=graph.replace("_"," ").title() gr=graph.replace("-","_").replace(":","_") - graphdefaults={"graph_title":gr_pretty,"graph_vlabel":gr_pretty,"graph_category":"osd"} + graphdefaults={"graph_title":gr_pretty,"graph_vlabel":gr_pretty,"graph_category":"fs"} graphsettings=dict(graphdefaults.items()+graphsettings.items()) print "multigraph %s" % (gr_simple) print "\n".join(["%s %s" % setting for setting in graphsettings.items()]) @@ -182,4 +182,3 @@ else: for osd in osds: print "multigraph %s.osd%s" % (gr,osd) print "osd%s_%s.value %s" % (osd,gr,data[osd][graph]) - diff --git a/plugins/ceph/ceph_capacity b/plugins/ceph/ceph_capacity index 0133a193..42a9685c 100755 --- a/plugins/ceph/ceph_capacity +++ b/plugins/ceph/ceph_capacity @@ -38,7 +38,7 @@ CRITICAL_LEVEL=${critical_level:-"90"} if [ "$1" = "config" ]; then echo 'graph_title CEPH capacity' - echo 'graph_category ceph' + echo 'graph_category fs' echo 'graph_vlabel GB' echo 'graph_info CEPH cluster capacity' echo 'graph_args --base 1000 -l 0' diff --git a/plugins/ceph/ceph_osd b/plugins/ceph/ceph_osd index 02879f2e..9d208b53 100755 --- a/plugins/ceph/ceph_osd +++ b/plugins/ceph/ceph_osd @@ -30,7 +30,7 @@ fi if [ "$1" = "config" ]; then echo 'graph_title CEPH OSDs' - echo 'graph_category ceph' + echo 'graph_category fs' echo 'graph_vlabel nr' echo 'graph_info CEPH OSD up/down status' echo 'graph_scale no' diff --git a/plugins/change.org/changeorg_signature_count b/plugins/change.org/changeorg_signature_count new file mode 100755 index 00000000..b9e7ab8a --- /dev/null +++ b/plugins/change.org/changeorg_signature_count @@ -0,0 +1,103 @@ +#!/usr/bin/python3 +''' +=head1 NAME + +Munin Plugin to grab signature count for change.org petition given its ID +You need a valid API key for this petition + +=head2 CONFIGURATION + +# Sample: +[changeorg_signature_count] + env.APIkey xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + env.petitions 1727001 1727053 + +=head1 AUTHOR + +(c) 2017 Raphaël Droz +General Public Licence v3 or later + +=head1 LICENSE + +GPLv3 + +=head1 MAGIC MARKERS + +#%# family=auto +#%# capabilities=autoconf +''' + +from sys import argv, exit +import sys +import codecs +from os import environ, path, umask +import re +import urllib.request +import json + +if len(argv) > 1 and argv[1] == 'autoconf': + ok = True + if not environ.get('APIkey') or not re.match('[a-fA-F0-9]{64}$', environ.get('APIkey')): + print("no (env.APIkey not defined or bad format)") + ok = False + for i in environ.get("petitions"): + if not re.match("[0-9]+$", i): + print("no ($i isn't a valid petition ID") + ok = False + if ok: + print("yes") + exit(0) + + +petition_titles = {} +write_cache = False + +if environ.get('MUNIN_PLUGSTATE'): + petition_cache_names = path.join(environ['MUNIN_PLUGSTATE'], path.basename(argv[0]) + '-petition_names') + try: + with open(petition_cache_names, 'r') as f: + petition_titles = json.load(f) + except FileNotFoundError: + pass + +for i in environ.get('petitions').split(): + if i in petition_titles: + continue + # NB: user-agent's tweak is needed to avoid a 403 + req = urllib.request.Request("https://api.change.org/v1/petitions/{}?api_key={}&fields=title".format(i, environ.get('APIkey')), + data=None, + headers={ 'User-Agent': 'curl/7.38.0' }) + response = urllib.request.urlopen(req).read().decode('utf-8') + petition_titles[i] = json.loads(response) + write_cache = True + +if environ.get('MUNIN_PLUGSTATE') and write_cache: + umask(0o077) + with open(petition_cache_names, 'w') as outfile: + json.dump(petition_titles, outfile) + +# equivalent of passing PYTHONIOENCODING=utf-8 to munin +sys.stdout = codecs.getwriter("utf-8")(sys.stdout.detach()) + +if len(argv) > 1 and argv[1] == 'config': + print('''graph_title change.org signature count +graph_args --base 1000 -l 0 +graph_vlabel Signatures +graph_category other +graph_info change.org signature count +graph_period minute +''') + + for i in petition_titles: + print('''signcount_{pid}.label {title} +signcount_{pid}.info Total signatures for {title} +signcount_{pid}.draw LINE3 +signcount_{pid}.min 0'''.format(pid=i, title=petition_titles[i]["title"])) + exit(0) + +for i in environ.get("petitions").split(): + req = urllib.request.Request("https://api.change.org/v1/petitions/{}?api_key={}&fields=signature_count".format(i, environ.get('APIkey')), + data=None, + headers={ 'User-Agent': 'curl/7.38.0' }) + response = urllib.request.urlopen(req).read().decode('utf-8') + print("signcount_%s.value %s" % (i, json.loads(response)["signature_count"])) diff --git a/plugins/chat/tinychat_users_ b/plugins/chat/tinychat_users_ index 39a40679..d366b372 100755 --- a/plugins/chat/tinychat_users_ +++ b/plugins/chat/tinychat_users_ @@ -19,7 +19,7 @@ room=${0##*tinychat_users_} ## if [ "$1" = "autoconf" ]; then # Check that curl is installed - if hash curl >/dev/null 2>&1; then + if command -v curl >/dev/null 2>&1; then echo "yes" else echo "no (no curl installed)" diff --git a/plugins/cherokee/munin-plugin-for-cherokee b/plugins/cherokee/munin-plugin-for-cherokee index 8dbacac8..17e878bf 100755 --- a/plugins/cherokee/munin-plugin-for-cherokee +++ b/plugins/cherokee/munin-plugin-for-cherokee @@ -93,7 +93,7 @@ def munin_values(res): def munin_config(response): global type - print "graph_category cherokee" + print "graph_category webserver" if type == "rate": print "graph_title Cherokee Data Transfer Rate" print "graph_vlabel Bits sent(+) / received(-) per ${graph_period}" diff --git a/plugins/chilli/chilli_sessions_ b/plugins/chilli/chilli_sessions_ new file mode 100755 index 00000000..1e73f349 --- /dev/null +++ b/plugins/chilli/chilli_sessions_ @@ -0,0 +1,119 @@ +#!/bin/sh +# -*- sh -*- + +: << =cut + +=head1 NAME + +chilli_sessions_ - Wildcard-plugin to monitor sessions state on Coova Chilli. + +=head1 DESCRIPTION + +This wildcard plugin is for monitor the number of device with state pass/dnat/none on Coova Chilli instances. + +=head1 CONFIGURATION + +This plugin does not normally require configuration. + +The plugin may need to run as root. This is configured like this: + + [chilli_sessions_*] + user root + +This is a wildcard plugin. To monitor an instance, link +chilli_sessions_ to this file. For example : + + ln -s /usr/share/munin/plugins/chilli_sessions_ \ + /etc/munin/plugins/chilli_sessions_hotspot1 + +will monitor hotspot1. + +For monitor all instances use : + + ln -s /usr/share/munin/plugins/chilli_sessions_ \ + /etc/munin/plugins/chilli_sessions_total + +=head1 AUTHOR + +OPENevents - Guillaume Marsay + +=head1 LICENSE + +GPLv2 + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf suggest + +=cut + + +INSTANCE="${0##*chilli_}" +CHILLI_PATH_BIN="/usr/sbin/chilli_query" +CHILLI_PATH_SOCK="/var/run" + + +case "$1" in + autoconf) + if [ -r "$CHILLI_PATH_BIN" ]; then + if [ "$INSTANCE" = "total" ]; then + echo "yes" + exit 0 + else + if [ -r $CHILLI_PATH_SOCK/chilli_"$INSTANCE".sock ]; then + echo "yes" + exit 0 + else + echo "no ($CHILLI_PATH_SOCK/chilli_$INSTANCE.sock not found)" + exit 0 + fi + fi + else + echo "no ($CHILLI_PATH_BIN not found)" + exit 0 + fi + ;; + suggest) + find "$CHILLI_PATH_SOCK/" -name "chilli_*.sock" | while read file; do + basename "$file" .sock | cut -d _ -f 2 + done + + echo "total" + + exit 0 + ;; + config) + echo "graph_title Chilli $INSTANCE sessions" + echo "graph_args --base 1000 -l 0" + echo "graph_category wireless" + echo "none.label NONE" + echo "none.min 0" + echo "none.draw AREA" + echo "none.colour ff8000" + echo "dnat.label DNAT" + echo "dnat.min 0" + echo "dnat.draw STACK" + echo "dnat.colour 0066b3" + echo "pass.label PASS" + echo "pass.draw STACK" + echo "pass.min 0" + echo "pass.colour 00cc00" + + exit 0 + ;; +esac + +if [ "$INSTANCE" = "total" ]; then + STATE_PASS=$("$CHILLI_PATH_BIN" list | grep -wc "pass") + STATE_DNAT=$("$CHILLI_PATH_BIN" list | grep -wc "dnat") + STATE_NONE=$("$CHILLI_PATH_BIN" list | grep -wc "none") +else + STATE_PASS=$("$CHILLI_PATH_BIN" -s "$CHILLI_PATH_SOCK/chilli_$INSTANCE.sock" list | grep -wc "pass") + STATE_DNAT=$("$CHILLI_PATH_BIN" -s "$CHILLI_PATH_SOCK/chilli_$INSTANCE.sock" list | grep -wc "dnat") + STATE_NONE=$("$CHILLI_PATH_BIN" -s "$CHILLI_PATH_SOCK/chilli_$INSTANCE.sock" list | grep -wc "none") +fi + +echo "pass.value $STATE_PASS" +echo "dnat.value $STATE_DNAT" +echo "none.value $STATE_NONE" diff --git a/plugins/chrony/chrony b/plugins/chrony/chrony new file mode 100755 index 00000000..314860f6 --- /dev/null +++ b/plugins/chrony/chrony @@ -0,0 +1,119 @@ +#!/bin/sh + +: <<=cut + +=head1 NAME + +parse Chrony Tracking output for timeserver status information + +=head1 APPLICABLE SYSTEMS + +Any system with a local chronyd service. + +=head1 CONFIGURATION + +No configuration. + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=head1 VERSION + +Revision 0.1 2008/08/23 13:06:00 joti + + First version only chronyc tracking, autodetection included. + +Revision 0.2 2008/10/11 16:09:00 joti + + Added scaling of other values to match with frequency, added more description to fields + +Revision 0.3 2014/02/16 zjttoefs + + reduce forking by using awk + do not limit output precision + add stratum monitoring + detect slow/fast time or freqency and adjust sign of value accordingly + remove commented out code + +Revision 0.4 2016/11/10 Lars Kruse + + rewrite field handling + use "which" for "chronyc" location + switch from "bash" to "sh" + fix exit code of failing "autoconf" + +=head1 AUTHOR + + joti + zjttoefs + Lars Kruse + +=cut + +CHRONYC="$(which chronyc | head -1)" + +# Frequency has extremely higher values than other. Therefore they are fitted by scaling via suitable factors. +# field definitions: +# - munin fieldname +# - factor for graph visualization (all values are supposed to reach a similar dimension) +# - regular expression of the chrony output line (may not contain whitespace, case insensitive) +# - label (may include "%d" for including the factor; may contain whitespace) +fields="stratum 1 ^Stratum Stratum + systime 1000 ^System.time System Time (x%d) + frequency 1 ^Frequency Frequency (ppm) + residualfreq 100 ^Residual.freq Residual Freq (ppm, x%d) + skew 100 ^Skew Skew (ppm, x%d) + rootdelay 1000 ^Root.delay Root delay (seconds, x%d) + rootdispersion 1000 ^Root.dispersion Root dispersion (seconds, x%d)" + +# chrony example output (v2.4.1): +# Reference ID : 131.188.3.221 (ntp1.rrze.uni-erlangen.de) +# Stratum : 2 +# Ref time (UTC) : Thu Nov 10 22:39:50 2016 +# System time : 0.000503798 seconds slow of NTP time +# Last offset : +0.000254355 seconds +# RMS offset : 0.002186779 seconds +# Frequency : 17.716 ppm slow +# Residual freq : +0.066 ppm +# Skew : 4.035 ppm +# Root delay : 0.042980 seconds +# Root dispersion : 0.005391 seconds +# Update interval : 258.4 seconds +# Leap status : Normal + + +if [ "$1" = "autoconf" ]; then + if [ -n "$CHRONYC" ] && [ -x "$CHRONYC" ]; then + echo yes + else + echo "no (missing 'chronyc' executable)" + fi + exit 0 +fi + +if [ "$1" = "config" ]; then + echo 'graph_title Chrony Tracking Stats' + echo 'graph_args --base 1000 -l 0' + echo 'graph_vlabel (seconds,ppm)' + echo 'graph_category time' + echo "$fields" | while read fieldname factor regex label; do + # insert the factor, if "%d" is part of the label + printf "${fieldname}.label $label\n" "$factor" + echo "${fieldname}.type GAUGE" + done + exit 0 +fi + +chrony_status="$("$CHRONYC" tracking)" +echo "$fields" | while read fieldname factor regex label; do + status_line="$(echo "$chrony_status" | grep -i -- "$regex " | cut -d ":" -f 2-)" + if [ -z "$status_line" ]; then + value="U" + else + # the keyword "slow" indicates negative values + value="$(echo "$status_line" | awk '{ /slow/ ? SIGN=-1 : SIGN=1; print $1 * SIGN * '"$factor"' }')" + fi + echo "${fieldname}.value $value" +done diff --git a/plugins/mail/clamav b/plugins/clamav/clamav similarity index 95% rename from plugins/mail/clamav rename to plugins/clamav/clamav index 46565d77..042a3d4e 100755 --- a/plugins/mail/clamav +++ b/plugins/clamav/clamav @@ -18,7 +18,7 @@ if [ "$1" = "config" ]; then graph_title ClamAV stats graph_args --base 1000 -l 0 graph_vlabel virus/day -graph_category Mail +graph_category antivirus virus.label virus virus.type DERIVE virus.min 0 diff --git a/plugins/condor/condor_activity_ b/plugins/condor/condor_activity_ index 3f779b74..2ba11cb8 100755 --- a/plugins/condor/condor_activity_ +++ b/plugins/condor/condor_activity_ @@ -68,7 +68,7 @@ if [ "$1" = "config" ]; then echo "graph_vlabel VMs" echo "graph_scale no" echo "graph_info Shows slot activity from condor_status." - echo "graph_category Condor" + echo "graph_category htc" echo "graph_period second" echo "idl.label Idle" echo "idl.draw AREA" diff --git a/plugins/condor/condor_ops_ b/plugins/condor/condor_ops_ index 7df8ffae..6f663a81 100755 --- a/plugins/condor/condor_ops_ +++ b/plugins/condor/condor_ops_ @@ -68,7 +68,7 @@ if [ "$1" = "config" ]; then echo 'graph_vlabel MIPS, MFLOPS' echo 'graph_scale no' echo 'graph_info Shows MIPS and MFLOPS from condor_status.' - echo 'graph_category Condor' + echo 'graph_category htc' echo 'graph_period second' echo 'mips_cur.label MIPS claimed' echo 'mips_cur.draw LINE2' @@ -108,4 +108,3 @@ eval $CS $CONS -totals -server | awk 'BEGIN { mipsc=0; mflopsc=0 } /Total/ {mips # Machines MIPS KFLOPS AvgLoadAvg # Total 28 104576 29093286 0.960 eval $CS $CONS -totals -server -claimed | awk 'BEGIN { mipsc=0; mflopsc=0 } /Total/ { mipsc = $3; kflopsc = $4; mflopsc = int(kflopsc/1000) } END {print "mips_cur.value " mipsc "\nmflops_cur.value " mflopsc}' - \ No newline at end of file diff --git a/plugins/condor/condor_queue_ b/plugins/condor/condor_queue_ index c84ae4bf..ab9eabc5 100755 --- a/plugins/condor/condor_queue_ +++ b/plugins/condor/condor_queue_ @@ -70,7 +70,7 @@ if [ "$1" = "config" ]; then echo "graph_vlabel Jobs" echo "graph_scale no" echo "graph_info Shows global Condor queue from condor_q -g." - echo "graph_category Condor" + echo "graph_category htc" echo "graph_period second" echo "held.label Held" echo "held.draw AREA" @@ -93,4 +93,3 @@ fi # example: 3076 jobs; 3052 idle, 24 running, 0 held $CQ -g $OPTIONS | tail -n1 | awk '{print "queue.value " $1 "\nheld.value " $7 "\nrunning.value " $5 "\nidle.value " $3}' - \ No newline at end of file diff --git a/plugins/condor/condor_states_ b/plugins/condor/condor_states_ index e3910257..bfb27125 100755 --- a/plugins/condor/condor_states_ +++ b/plugins/condor/condor_states_ @@ -67,7 +67,7 @@ if [ "$1" = "config" ]; then echo 'graph_vlabel VMs' echo 'graph_scale no' echo 'graph_info Shows Condor slot states from condor_status.' - echo 'graph_category Condor' + echo 'graph_category htc' echo 'graph_period second' echo 'Preempting.label Preempting' echo 'Preempting.draw AREA' @@ -98,4 +98,3 @@ fi # x86_64/LINUX 8 0 8 0 0 0 0 # Total 30 1 26 3 0 0 0 eval $CS $CONS -total | awk '/Total / {print "Preempting.value " $7 "\nClaimed.value " $4 "\nMatched.value " $6 "\nUnclaimed.value " $5 "\nOwner.value " $3 }' - \ No newline at end of file diff --git a/plugins/mail/courier_log b/plugins/courier/courier_log similarity index 100% rename from plugins/mail/courier_log rename to plugins/courier/courier_log diff --git a/plugins/cpan/cpanp_o b/plugins/cpan/cpanp_o index 3d30ebd3..bb163fe1 100755 --- a/plugins/cpan/cpanp_o +++ b/plugins/cpan/cpanp_o @@ -7,7 +7,7 @@ given ($ARGV[0]) { graph_title CPAN modules not up to date graph_args --base 1000 -l 0 graph_vlabel modules -graph_category CPAN +graph_category security graph_info The number of installed CPAN modules that aren't up to date count.label modules count.type GAUGE diff --git a/plugins/system/cpu-usage-by-process b/plugins/cpu/cpu-usage-by-process similarity index 97% rename from plugins/system/cpu-usage-by-process rename to plugins/cpu/cpu-usage-by-process index b00b9860..ae6469a6 100755 --- a/plugins/system/cpu-usage-by-process +++ b/plugins/cpu/cpu-usage-by-process @@ -37,7 +37,7 @@ fi if [ "$1" = "config" ] ; then echo "graph_args --base 1000 -r --lower-limit 0"; echo "graph_title CPU usage, by process"; - echo "graph_category system"; + echo "graph_category processes"; echo "graph_info This graph shows CPU usage, for monitored processes."; echo 'graph_vlabel %' echo 'graph_scale no' diff --git a/plugins/system/cpu_ b/plugins/cpu/cpu_ similarity index 100% rename from plugins/system/cpu_ rename to plugins/cpu/cpu_ diff --git a/plugins/system/cpu_by_process b/plugins/cpu/cpu_by_process similarity index 99% rename from plugins/system/cpu_by_process rename to plugins/cpu/cpu_by_process index 98b066ad..ee39f966 100755 --- a/plugins/system/cpu_by_process +++ b/plugins/cpu/cpu_by_process @@ -86,7 +86,7 @@ if (@ARGV and $ARGV[0] eq "config") graph_title CPU time by Process graph_args --base 1000 graph_vlabel seconds -graph_category system +graph_category processes graph_info Shows CPU time used by each process name END diff --git a/plugins/processes/cpuload_ b/plugins/cpu/cpuload_ similarity index 100% rename from plugins/processes/cpuload_ rename to plugins/cpu/cpuload_ diff --git a/plugins/processes/cpuutil b/plugins/cpu/cpuutil similarity index 100% rename from plugins/processes/cpuutil rename to plugins/cpu/cpuutil diff --git a/plugins/system/multicpu1sec/multicpu1sec-days.png b/plugins/cpu/example-graphs/multicpu1sec-c.c-days.png similarity index 100% rename from plugins/system/multicpu1sec/multicpu1sec-days.png rename to plugins/cpu/example-graphs/multicpu1sec-c.c-days.png diff --git a/plugins/system/multicpu1sec/multicpu1sec-hours.png b/plugins/cpu/example-graphs/multicpu1sec-c.c-hours.png similarity index 100% rename from plugins/system/multicpu1sec/multicpu1sec-hours.png rename to plugins/cpu/example-graphs/multicpu1sec-c.c-hours.png diff --git a/plugins/system/multicpu1sec/multicpu1sec-minutes.png b/plugins/cpu/example-graphs/multicpu1sec-c.c-minutes.png similarity index 100% rename from plugins/system/multicpu1sec/multicpu1sec-minutes.png rename to plugins/cpu/example-graphs/multicpu1sec-c.c-minutes.png diff --git a/plugins/processes/multicpu b/plugins/cpu/multicpu similarity index 99% rename from plugins/processes/multicpu rename to plugins/cpu/multicpu index 2b5ee4ab..f0998f5e 100755 --- a/plugins/processes/multicpu +++ b/plugins/cpu/multicpu @@ -1,5 +1,5 @@ #!/usr/bin/php -_ + +=head1 SYNOPSIS + + ln -s /usr/share/munin/plugins/ethermine_hashrate_ \ + /etc/munin/plugins/ethermine_hashrate_3257bde8cf067ae6f1ddc0e4b140fe02e3c5e44f_mine + +=head1 INTERPRETATION + +This plugin shows the ethermine.org mining pool hashrate (MH/s) of a given ethereum address and rig name. +Hashrate is queried via ethermine.org API L. + +=head1 VERSION + +0.0.1 + +=head1 AUTHOR + +L + +=head1 LICENSE + +GPLv2 + +=head1 MAGIC MARKERS + + #%# family=manual + +=cut +""" + +from __future__ import print_function + +import sys +import json +import codecs + +try: + # python3 + from urllib.request import urlopen + from urllib.request import Request +except ImportError: + # python2 + from urllib2 import urlopen + from urllib2 import Request + +command = '' +if len(sys.argv) > 1: + command = sys.argv[1] + +try: + eth_address, miner = sys.argv[0].split("_")[2:] +except ValueError: + print("The filename of this plugin (or its symlink) should follow this pattern: " + "'ethermine_hashrate__'", file=sys.stderr) + sys.exit(9) + +if command == 'config': + print("graph_title Ethermine {}".format(miner)) + print("graph_info ethermine.org Mining Pool Hashrate for {}_{}".format(eth_address, miner)) + print("graph_vlabel Ethermine Hashrate") + print("graph_category htc") + print("ethermine_mhs_{}_{}.warning 20:".format(eth_address, miner)) + print("ethermine_mhs_{}_{}.critical 10:".format(eth_address, miner)) + print("ethermine_mhs_{}_{}.label MH/s:".format(eth_address, miner)) + sys.exit(0) + + +ethermine_api_url = 'https://ethermine.org/api/miner_new/' + eth_address + +mining_req = Request(ethermine_api_url) +# User-Agent to bypass Cloudflare +mining_req.add_header('User-Agent', 'Ethermine Munin Plugin/1.0') + +try: + mining_stats_raw = urlopen(mining_req, timeout=15) +except IOError as exc: + print("Failed to request ethermine.org API: {}".format(exc), file=sys.stderr) + sys.exit(9) + +reader = codecs.getreader("utf-8") + +try: + mining_stats = json.load(reader(mining_stats_raw)) +except ValueError: + # Error in decoding operation or in JSON parsing throw a ValueError + print("Failed to parse JSON response.", file=sys.stderr) + sys.exit(9) + +try: + workers = mining_stats['workers'] +except: + print("JSON result error!", file=sys.stderr) + sys.exit(9) + +# ethermine.org sometimes has caching errors. You can see data from other miner. Always check your rig name. +for worker in workers: + if workers[worker]['worker'] == miner: + hash_rate = workers[worker]['hashrate'] + hash_rate = hash_rate.replace(" MH/s", "") + print("ethermine_mhs_{}_{}.value {}".format(eth_address, miner, hash_rate)) diff --git a/plugins/currency/ethereum/etherscan_balance_ b/plugins/currency/ethereum/etherscan_balance_ new file mode 100755 index 00000000..03c323d3 --- /dev/null +++ b/plugins/currency/ethereum/etherscan_balance_ @@ -0,0 +1,117 @@ +#!/usr/bin/env python3 + +""" +=head1 NAME + +etherscan_balance_ - Munin plugin to monitor your ethereum (ETH) balance + +=head1 APPLICABLE SYSTEMS + +All systems with "python" and "munin" + +=head1 CONFIGURATION + +etherscan_balance_ + +=head1 SYNOPSIS + + ln -s /usr/share/munin/plugins/etherscan_balance_ \ + /etc/munin/plugins/etherscan_balance_0x3257bde8cf067ae6f1ddc0e4b140fe02e3c5e44f + +=head1 INTERPRETATION + +This plugin shows the ether balance (ETH) of a given ethereum address. +Account balance is queried via etherscan.io API L. + +=head1 VERSION + +0.0.1 + +=head1 AUTHOR + +L + +=head1 LICENSE + +GPLv2 + +=head1 MAGIC MARKERS + + #%# family=manual + +=cut +""" + +from __future__ import print_function + +import sys +import json +import codecs + +try: + # python3 + from urllib.request import urlopen + from urllib.request import Request +except ImportError: + # python2 + from urllib2 import urlopen + from urllib2 import Request + +command = '' +if len(sys.argv) > 1: + command = sys.argv[1] + + +api_action, eth_address = sys.argv[0].split("_")[1:] + +if not eth_address: + print("The filename of this plugin (or its symlink) should follow this pattern: " + "'etherscan_balance_'", file=sys.stderr) + sys.exit(9) + +""" +API result is in Wei. Convert Wei to Ether (ETH) via 'fieldname.cdef' +1 : Wei +10^12 : Szabo +10^15 : Finney +10^18 : Ether +233874700000000000000000 Wei = 233,874.7 Ether +""" +if command == 'config': + print("graph_title Ether {}".format(eth_address)) + print("graph_info Ethereum Account Balance for Address {}".format(eth_address)) + print("graph_vlabel Ethereum Balance") + print("graph_category htc") + print("wei_balance_{0}.cdef wei_balance_{0},1000000000000000000,/".format(eth_address)) + print("wei_balance_{}.label ETH".format(eth_address)) + sys.exit(0) + +ethercan_balance_api_url = ('https://api.etherscan.io/api' + '?module=account&action=balance&tag=latest&address=' + eth_address) + +etherscan_req = Request(ethercan_balance_api_url) +# User-Agent to bypass Cloudflare +etherscan_req.add_header('User-Agent', 'Etherscan Munin Plugin/1.0') + +try: + etherscan_balance_raw = urlopen(etherscan_req, timeout=15) +except IOError as exc: + print("Failed to request etherscan.io API: {}".format(exc), file=sys.stderr) + sys.exit(9) + +reader = codecs.getreader("utf-8") + +try: + etherscan_balance = json.load(reader(etherscan_balance_raw)) +except ValueError: + # Error in decoding operation or in JSON parsing throw a ValueError + print("Failed to parse JSON response.", file=sys.stderr) + sys.exit(9) + +try: + eth = int(etherscan_balance['result']) +except (KeyError, ValueError) as exc: + print("JSON result error!", file=sys.stderr) + sys.exit(9) + +print("wei_balance_{}.value {}".format(eth_address, eth)) diff --git a/plugins/currency/nanopool/nanopool_ b/plugins/currency/nanopool/nanopool_ new file mode 100755 index 00000000..3bc8bed0 --- /dev/null +++ b/plugins/currency/nanopool/nanopool_ @@ -0,0 +1,295 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# vim: set fileencoding=utf-8 + +""" +=head1 NAME + +nanopool_ - Munin plugin to monitor nanopool ethereum user data. + + +=head1 CONFIGURATION + +[nanopool_*] + + - Copy to /usr/share/munin/plugins + - Create symlinks in /etc/munin/plugins to nanopool__, + e.g. ln -s /usr/share/munin/plugins/nanopool_ /etc/munin/plugins/nanopool_hashrate_0x1234567890abcdef1234567890 + Please use the account address in the format "0x
". + + - To enable graphs, link to one or more of the graph types available + - Then restart munin-node + +Graph types and their links: + balance: link to nanopool_balance Show current balance + hashrate: link to nanopool_hashrate Show current calculated hashrate + avghashrate: link to nanopool_avghashrate Show average hashrate of last hour + worker: link to nanopool_worker Show worker data + + +Thanks to the authors of other Python munin plugins. I've used some of +them as inspiring example. + +=head1 VERSION +1.0.1 + +=head1 AUTHOR +L + +=head1 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, 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 MAGIC MARKERS + +#%# family=contrib +#%# capabilities=suggest + +=cut +""" + +from __future__ import print_function + +import os +import sys +import json + +try: + # Python 3 + from urllib.request import Request + from urllib.request import urlopen + from urllib.request import URLError +except: + # Python 2 + from urllib2 import Request + from urllib2 import urlopen + from urllib2 import URLError + + + +def define_graph_types(): + graph_types = { + "balance" : [ + { + "title" : "Balance of {0}".format(account_address), + "type" : "GAUGE", + "args" : "--base 1000 -l 0", + "fields" : ["ETH"], + "scale": "no", + "info": "Balance in ETH" + } + ], + "hashrate" : [ + { + "title" : "Hashrate of {0}".format(account_address), + "type" : "GAUGE", + "args" : "--base 1000 -l 0", + "fields" : ["hashrate"], + "info": "Current Calculated Hashrate in Mh/s" + } + ], + "avghashrate" : [ + { + "title" : "Average Hashrate of {0}".format(account_address), + "type" : "GAUGE", + "args" : "--base 1000 -l 0", + "fields" : ["avg_hashrate"], + "info": "Average Hashrate of last hour in Mh/s" + + } + ], + "worker" : [ + { + "title" : "Worker" + } + ] + } + return graph_types + + +def request_data(): + + url = "https://api.nanopool.org/v1/eth/user/{0}".format(account_address) + + req = Request(url) + # this fake is necessary to get values, otherwise the request ends in a 403 error + req.add_header('User-Agent', 'Nozilla/5.0') + try: + txt = urlopen(req).read() + except URLError as err: + print("API request error: {0}". format(err), file=sys.stderr) + exit(1) + except: + print("Unhandled error:", sys.exc_info()[0]) + exit(1) + try: + result = json.loads(txt.decode("utf-8")) + except ValueError as err: + print("Could not decode JSON error: {0}". format(err), file=sys.stderr) + exit(1) + return result + +def write_config_worker(): + data = request_data() + worker_data = sorted(data["data"]["workers"], key=lambda element: element["id"]) + + print("multigraph worker_hashrate_{0}".format(account_address)) + print("graph_title Hashrate in Mh/s per worker ({0})".format(account_address)) + print("graph_args --base 1000 -l 0") + print("graph_vlabel Mh/s") + print("graph_category htc") + print("graph_scale no") + + for val in worker_data: + worker_name = "_".join(val["id"].split()) + print("worker_{0}_hashrate.label {1}".format(worker_name, val["id"])) + print("worker_{0}_hashrate.type GAUGE".format(worker_name)) + print("worker_{0}_hashrate.info Hashrate of worker '{1}'".format(worker_name, val["id"])) + print("worker_{0}_hashrate.min 0".format(worker_name)) + print("worker_{0}_hashrate.draw LINE1".format(worker_name)) + + for val in worker_data: + print("") + worker_name = "_".join(val["id"].split()) + print("multigraph worker_hashrate_{0}.worker_{1}".format(account_address, worker_name)) + print("graph_title Hashrate in Mh/s of worker {0}".format(worker_name)) + print("graph_args --base 1000 -l 0") + print("graph_vlabel Mh/s") + print("graph_category htc") + print("graph_scale no") + print("whashrate.label hashrate") + print("whashrate.type GAUGE") + print("whashrate.info Hashrate of worker {0}".format(val["id"])) + print("whashrate.min 0") + print("whashrate.draw LINE1") + + print("") + print("multigraph worker_shares_{0}".format(account_address)) + print("graph_title Number of accepted shares ({0})".format(account_address)) + print("graph_args --base 1000 -l 0") + print("graph_vlabel Shares per ${graph_period}") + print("graph_category htc") + print("graph_scale no") + print("graph_period minute") + + for val in worker_data: + worker_name = "_".join(val["id"].split()) + print("worker_{0}_shares.label {1} ".format(worker_name, val["id"])) + print("worker_{0}_shares.type COUNTER".format(worker_name)) + print("worker_{0}_shares.info Accepted shares of worker '{1}'".format(worker_name, val["id"])) + print("worker_{0}_shares.min 0".format(worker_name)) + print("worker_{0}_shares.draw LINE1".format(worker_name)) + + for val in worker_data: + worker_name = "_".join(val["id"].split()) + print("") + print("multigraph worker_shares_{0}.worker_{1}".format(account_address, worker_name)) + print("graph_title Number of accepted shares {0}".format(worker_name)) + print("graph_args --base 1000 -l 0") + print("graph_vlabel Shares per ${graph_period}") + print("graph_category htc") + print("graph_scale no") + print("graph_period minute") + print("wshares.label shares") + print("wshares.type COUNTER") + print("wshares.info Accepted shares of worker '{0}'".format(val["id"])) + print("wshares.min 0") + print("wshares.draw LINE1") + + +def write_data_worker(data): + worker_data = sorted(data["data"]["workers"], key=lambda element: element["id"]) + + print("multigraph worker_hashrate_{0}".format(account_address)) + + for val in worker_data: + worker_name = "_".join(val["id"].split()) + print("worker_{0}_hashrate.value {1}".format(worker_name, val["hashrate"])) + + for val in worker_data: + print("") + worker_name = "_".join(val["id"].split()) + print("multigraph worker_hashrate_{0}.worker_{1}".format(account_address, worker_name)) + print("whashrate.value {0}".format(val["hashrate"])) + + print("") + print("multigraph worker_shares_{0}".format(account_address)) + for val in worker_data: + worker_name = "_".join(val["id"].split()) + print("worker_{0}_shares.value {1}".format(worker_name, val["rating"])) + + for val in worker_data: + worker_name = "_".join(val["id"].split()) + print("") + print("multigraph worker_shares_{0}.worker_{1}".format(account_address, worker_name)) + print("wshares.value {0} ".format(val["rating"])) + + + +def write_config(): + if graph_type not in GRAPH_TYPES.keys(): + print("Unknown graph type '{0}'".format(graph_type), file=sys.stderr) + exit(1) + if graph_type == "worker": + write_config_worker() + return + params = GRAPH_TYPES[graph_type] + for item in params: + print("graph_title {0}".format(item["title"])) + print("graph_category htc") + if "info" in item: + print("graph_info {0}".format(item["info"])) + if "scale" in item: + print("graph_scale {0}".format(item["scale"])) + if "args" in item: + print("graph_args {0}".format(item["args"])) + for field in item["fields"]: + print("{0}.label {1}".format(field, field)) + print("{0}.type {1}".format(field, item["type"])) + +def write_suggest(): + for item in GRAPH_TYPES.keys(): + print(item) + +def write_data(): + data = request_data() + + if graph_type == "balance": + print("ETH.value {0}".format(data['data']['balance'])) + elif graph_type == "hashrate": + print("hashrate.value {0}".format(data["data"]["hashrate"])) + elif graph_type == "avghashrate": + print("avg_hashrate.value {0}".format(data["data"]["avgHashrate"]["h1"])) + elif graph_type == "worker": + write_data_worker(data) + else: + pass + + +if __name__ == "__main__": + + program = sys.argv[0] + try: + graph_type, account_address = program.split("_")[1:] + except ValueError: + print("Please configure the plugin as described in the configuration section.") + exit(1) + + GRAPH_TYPES = define_graph_types() + if len(sys.argv) > 1 and sys.argv[1] == "config": + write_config() + elif len(sys.argv) > 1 and sys.argv[1] == "suggest": + write_suggest() + else: + write_data() diff --git a/plugins/cyrus/cyrus-imapd b/plugins/cyrus/cyrus-imapd index e2b54e3a..4492b7a1 100755 --- a/plugins/cyrus/cyrus-imapd +++ b/plugins/cyrus/cyrus-imapd @@ -94,7 +94,7 @@ if [ "$1" = "config" ]; then echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel connections' echo 'graph_scale no' - echo 'graph_category cyrus' + echo 'graph_category mail' echo 'graph_info Current connections to the imap server. bawue.net e.V. Trac repository.' echo 'graph_order connections authenticated_users unique_users' echo 'connections.label Connections' diff --git a/plugins/system/dar_cpuusage b/plugins/darwin/dar_cpuusage similarity index 100% rename from plugins/system/dar_cpuusage rename to plugins/darwin/dar_cpuusage diff --git a/plugins/system/dar_memusage b/plugins/darwin/dar_memusage similarity index 100% rename from plugins/system/dar_memusage rename to plugins/darwin/dar_memusage diff --git a/plugins/system/dar_swap b/plugins/darwin/dar_swap similarity index 100% rename from plugins/system/dar_swap rename to plugins/darwin/dar_swap diff --git a/plugins/system/dar_uptime b/plugins/darwin/dar_uptime similarity index 100% rename from plugins/system/dar_uptime rename to plugins/darwin/dar_uptime diff --git a/plugins/system/dar_vmstat b/plugins/darwin/dar_vmstat similarity index 100% rename from plugins/system/dar_vmstat rename to plugins/darwin/dar_vmstat diff --git a/plugins/network/dar_vpnd b/plugins/darwin/dar_vpnd similarity index 96% rename from plugins/network/dar_vpnd rename to plugins/darwin/dar_vpnd index 660d82e5..f454429b 100755 --- a/plugins/network/dar_vpnd +++ b/plugins/darwin/dar_vpnd @@ -31,7 +31,7 @@ use strict; my $cmd = "ps -ef | awk '/[p]ppd/ {print substr(\$NF,2);}' | wc -l"; if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { - print "graph_category VPN\n"; + print "graph_category network\n"; print "graph_args --base 1024 -r --lower-limit 0\n"; print "graph_title Number of VPN Connections\n"; print "graph_vlabel VPN Connections\n"; @@ -41,4 +41,4 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { } else { my $output = `$cmd`; print "connections.value $output"; -} \ No newline at end of file +} diff --git a/plugins/db2/db2_cnx b/plugins/db2/db2_cnx index d6d60ad1..9a8ace0d 100755 --- a/plugins/db2/db2_cnx +++ b/plugins/db2/db2_cnx @@ -7,7 +7,7 @@ . /home/db2inst1/sqllib/db2profile echo "graph_title Number of connections" -echo "graph_category DB2" +echo "graph_category db" echo "graph_args -l 0" db2 list application | tail +5 | awk ' /^[A-Z]/ { print $1 }' | sort | uniq -c > $HOME/run/$(basename $0).txt diff --git a/plugins/debian/deborphan b/plugins/debian/deborphan new file mode 100755 index 00000000..10d5ac44 --- /dev/null +++ b/plugins/debian/deborphan @@ -0,0 +1,83 @@ +#!/bin/sh + +: << =cut + +=head1 NAME + +deborphan - Monitor orphaned Debian packages + +=head1 APPLICABLE SYSTEMS + +Debian-ish systems with deborphan installed. + +=head1 CONFIGURATION + +None needed. + +=head1 AUTHOR + +Olivier Mehani +Based on the debsecan plugin by Nicolas Bouthors. 2016-09-02 + +=head1 LICENSE + +GPLv2 + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=cut + +# shellcheck disable=SC1090 +. "$MUNIN_LIBDIR/plugins/plugin.sh" + +# Auto enable if we have deborphan only +if [ "$1" = "autoconf" ] ; then + if which deborphan >/dev/null; then + echo yes + else + echo "no (deborphan is missing)" + fi + exit 0 +fi + +# Fail if we don't have deborphan +if ! which deborphan >/dev/null; then + echo "deborphan is missing" >&2 + exit 1 +fi + +OUT=$(deborphan --show-section --guess-all | sort) + +CATEGORIES="$(echo "${OUT}" | awk '{print $1}' | uniq)" + +if [ "$1" = "config" ]; then + cat < http://nbi.fr/, Inspiration of the moment 10/10/2007 +* Olivier Mehani , 2016 + +=head1 LICENSE + +Public Domain + +=head1 MAGIC MARKERS + +%# family=auto +%# capabilities=autoconf + +=cut + +# Auto enable if we have debsecan only +if [ "$1" = "autoconf" ] ; then + if [ -x /usr/bin/debsecan ]; then + echo yes + else + echo 'no (/usr/bin/debsecan not found)' + fi + exit 0 +fi + +# Fail if we don't have debsecan +if [ ! -x /usr/bin/debsecan ]; then + echo 'error: /usr/bin/debsecan not found' >&2 + exit 1 +fi + +SUITE=${suite:-sid} +FIXEDWARN=${fixed_warning:-1} +FIXEDCRIT=${fixed_critical:-1000} +REMOTEWARN=${remote_warning:-1} +REMOTECRIT=${remote_critical:-10} + +MODE=$(echo "$0" | sed 's/.*_//') +case "${MODE}" in + 'cve') + TITLE_ADD="unique " + FIELD=1 + ;; + 'pkg' | *) + TITLE_ADD="package " + FIELD=2 + ;; +esac + +if [ "$1" = "config" ] ; then + cat < /dev/null) +REMOTE=$(echo "$ALL" | grep -w 'remotely') +NONREMOTE=$(echo "$ALL" | grep -wv 'remotely') + +HIGH=$(echo "${NONREMOTE}" | grep -w 'high urgency') +MEDIUM=$(echo "${NONREMOTE}" | grep -w 'medium urgency') +LOW=$(echo "${NONREMOTE}" | grep -w 'low urgency') +OTHER=$(echo "${NONREMOTE}" | grep -wv 'urgency') +FIXED=$(echo "${ALL}" | grep -w '(fixed') + +# Arguments: Field offset to aggregate by +count_entries() { + CUT_FIELD="${1}" + cut -f "${CUT_FIELD}" -d " "| sort | uniq -c +} + +case "${MODE}" in + 'cve') + remote_count=$(echo "${REMOTE}" | count_entries "${FIELD}" | wc -l) + high_count=$(echo "${HIGH}" | count_entries "${FIELD}" | wc -l) + medium_count=$(echo "${MEDIUM}" | count_entries "${FIELD}" | wc -l) + low_count=$(echo "${LOW}" | count_entries "${FIELD}" | wc -l) + other_count=$(echo "${OTHER}" | count_entries "${FIELD}" | wc -l) + fixed_count=$(echo "${FIXED}" | count_entries "${FIELD}" | wc -l) + ;; + 'pkg' | *) + remote_count=$(echo "${REMOTE}" | wc -l) + high_count=$(echo "${HIGH}" | wc -l) + medium_count=$(echo "${MEDIUM}" | wc -l) + low_count=$(echo "${LOW}" | wc -l) + other_count=$(echo "${OTHER}" | wc -l) + fixed_count=$(echo "${FIXED}" | wc -l) + ;; +esac + +# Reformat the output of the cut|sort|uniq... to a more human-friendly "item (count)" format +CVECOUNTRE="s/^ *\([0-9]\+\) \+\([^ ]\+\)/\2 (\1)/" + +# shellcheck disable=SC2005 disable=SC2046 +# The nested $(echo ...)s are needed to yet the newlines +cat < + +=head1 LICENSE + +GPLv2 + +=cut + +use strict; +use Munin::Plugin; + +if ($ARGV[0] and $ARGV[0] eq 'autoconf') { + if (-e "/sbin/btrfs") { + print "yes\n"; + } else { + print "no (/sbin/btrfs is missing)\n"; + } + exit 0; +} + +my @fsroot; +if ( !defined( $ENV{"fsroot"} ) ) { + die "No fsroot given! See the manual at top of this plugin file."; +} +@fsroot = $ENV{"fsroot"}; + +# get subvolumes +my %subvols; +open(SVS, "btrfs subvolume list --sort=path @fsroot |") + or die("Failed to run 'btrfs subvolume list': " . $!); +while (my $line = ) { + chomp $line; + $line =~ s/ID ([0-9]+) gen ([0-9]+) top level ([0-9]+) path (.+)//; + $subvols{$1}->{id} = $1; + $subvols{$1}->{name} = $4; +} +close SVS; + +# get sizes from quota +open(QGS, "btrfs qgroup show @fsroot |") + or die("Failed to run 'btrfs qgroup show': " . $!); +while (my $line = ) { + chomp $line; + $line =~ s|([0-9]+)/([0-9]+)\s+([0-9]+)\s+([0-9]+)||; + if (defined($2) && defined($subvols{$2})) { + $subvols{$2}->{rfer} = $3; + $subvols{$2}->{excl} = $4; + } +} +close QGS; + +# print config +if ($ARGV[0] and $ARGV[0] eq 'config') { + print "btrfs_usage\n"; + print "graph_title BTRFS Subvolume usage\n"; + print "graph_args --base 1024 --lower-limit 0\n"; + print "graph_vlabel Bytes\n"; + print "graph_category disk\n"; + + for my $id (sort { $subvols{$a}->{name} cmp $subvols{$b}->{name} } keys %subvols) { + print "$id.label " . $subvols{$id}->{name} . "\n"; + print "$id.type GAUGE\n"; + print "$id.draw LINE2\n"; + } + + exit 0; +} + +# print usage +print "btrfs_subvol_usage\n"; +for my $id (sort { $subvols{$a}->{name} cmp $subvols{$b}->{name} } keys %subvols) { + print "$id.value " . $subvols{$id}->{rfer} . "\n"; +} diff --git a/plugins/disk/dirsizes b/plugins/disk/dirsizes index a0c57dda..1d417847 100755 --- a/plugins/disk/dirsizes +++ b/plugins/disk/dirsizes @@ -16,7 +16,7 @@ # env.watchdirs /var/www,/tmp # # Change the env.watchdirs-variable according to your wishes. -# DONT FORGET TO RUN AS ROOT! +# DON'T FORGET TO RUN AS ROOT! # # You can test this plugin by calling it with params "test" and your watchdirs: # ./dirsizes test /dir1,/tmp/dir2 @@ -26,6 +26,7 @@ # use strict; +use Munin::Plugin; my @watchdirs; if ( exists $ARGV[0] and $ARGV[0] eq "test" ) { @@ -59,8 +60,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { foreach my $dir (@watchdirs) { # Remove illegal characters - my $label = $dir; - $label =~ s@[\/-]@_@g; + my $label = clean_fieldname($dir); # Print name print "dir", $label, ".label ", $dir, "\n"; @@ -72,17 +72,12 @@ else { # All available directories foreach my $dir (@watchdirs) { - # Remove illegal characters - my $label = $dir; - $label =~ s@[\/-]@_@g; + my $label = clean_fieldname($dir); # Get the dirsize my $dirsize = getSize($dir); - # Get the label - my $label = niceLabelname($dir); - # Print name print "dir", $label, ".value ", $dirsize, ".0\n"; } @@ -97,13 +92,5 @@ sub getSize { return @dirsize[0]; } -# Remove illegal characters -sub niceLabelname { - my ($label) = @_; - - $label =~ s@[\/-]@_@g; - return $label; -} - exit 0; diff --git a/plugins/disk/disk-io-multigraph-munin-plugin b/plugins/disk/disk-io-multigraph-munin-plugin index e2855bd5..dfa8811e 100755 --- a/plugins/disk/disk-io-multigraph-munin-plugin +++ b/plugins/disk/disk-io-multigraph-munin-plugin @@ -1,2 +1,20 @@ -Check http://aouyar.github.com/PyMunin/ to get the most recent versionof the -PyMunin Multi graph Munin Plugins and documentation. \ No newline at end of file +graph_category other + +=head1 NAME + +PyMunin - Multigraph Plugins using Python + +=head1 DESCRIPTION + +Check http://aouyar.github.com/PyMunin/ to get the most recent version of the +PyMunin Multi graph Munin Plugins and documentation. + +=head1 AUTHOR + +PyMunin has been developed by Ali Onur Uyar (https://github.com/aouyar)). + +=head1 LICENSE + +PyMunin is copyrighted free software made available under the terms of the GPL License Version 3 or later. + +=cut diff --git a/plugins/disk/du b/plugins/disk/du index bc1d4f84..f0133d49 100755 --- a/plugins/disk/du +++ b/plugins/disk/du @@ -35,7 +35,7 @@ ID=1; # [du] # user root # -# - by default the value is in MegaBytes, to change it you should edit below line in the script to something else, recognizeable by du (see man du) +# - by default the value is in MegaBytes, to change it you should edit below line in the script to something else, recognizable by du (see man du) # du -sm $DIR in MB # du -sk $DIR in KB # diff --git a/plugins/disk/du-2 b/plugins/disk/du-2 index 41e4adc1..6b8dfddc 100755 --- a/plugins/disk/du-2 +++ b/plugins/disk/du-2 @@ -91,12 +91,12 @@ if( (defined $ARGV[0]) && ($ARGV[0] eq "config") ) { while(defined (my $bar = )) { if ($bar =~ m/(\d+)\s+(.+)/) { my $dir = $2; - clean_path(\$dir); - print "$dir.label $dir\n"; + my $clean_dir = clean_fieldname($dir); + print "$clean_dir.label $dir\n"; if ($foo++) { - print "$dir.draw STACK\n"; + print "$clean_dir.draw STACK\n"; } else { - print "$dir.draw AREA\n"; + print "$clean_dir.draw AREA\n"; } } } @@ -110,8 +110,7 @@ open (FILE, "<", $CACHEFILE) or munin_exit_fail(); while(defined (my $foo = )) { if ($foo =~ m/(\d+)\s+(.+)/) { my ($field, $value) = ($2, $1); - clean_path(\$field); - print $field, ".value ", $value, "\n"; + print clean_fieldname($field), ".value ", $value, "\n"; } } close(FILE); diff --git a/plugins/disk/e2 b/plugins/disk/e2 old mode 100644 new mode 100755 diff --git a/plugins/disk/example-graphs/quota2percent_-day.png b/plugins/disk/example-graphs/quota2percent_-day.png new file mode 100644 index 00000000..16328631 Binary files /dev/null and b/plugins/disk/example-graphs/quota2percent_-day.png differ diff --git a/plugins/disk/hdsentinel b/plugins/disk/hdsentinel old mode 100644 new mode 100755 diff --git a/plugins/hp/hp_temp b/plugins/disk/hp_temp similarity index 100% rename from plugins/hp/hp_temp rename to plugins/disk/hp_temp diff --git a/plugins/sensors/hpasmcli2_ b/plugins/disk/hpasmcli2_ similarity index 100% rename from plugins/sensors/hpasmcli2_ rename to plugins/disk/hpasmcli2_ diff --git a/plugins/disk/lvm_ b/plugins/disk/lvm_ index 6c2ba72f..dad888ec 100755 --- a/plugins/disk/lvm_ +++ b/plugins/disk/lvm_ @@ -44,6 +44,9 @@ munin-node. EOF +. $MUNIN_LIBDIR/plugins/plugin.sh + + if [ "$1" = "autoconf" ]; then if ! command -v lvs >/dev/null; then echo "no (lvs not found)" @@ -64,7 +67,7 @@ fi vg=`echo $0 | awk '{ sub(".*lvm_","",\$1); print \$1; }'` clean_name() { - echo $1 | sed 's/[\/.-]/_/g' + echo "$(clean_fieldname "$1")" } diff --git a/plugins/disk/lvm_snap_used b/plugins/disk/lvm_snap_used index 87574a24..91a34012 100755 --- a/plugins/disk/lvm_snap_used +++ b/plugins/disk/lvm_snap_used @@ -18,6 +18,9 @@ # 2012/01/27 - Sébastien Gross # - Fix lvdisplay path + +. $MUNIN_LIBDIR/plugins/plugin.sh + lvdisplay=$(which lvdisplay) if [ "$1" = "autoconf" ]; then @@ -35,10 +38,20 @@ if [ "$1" = "config" ]; then echo 'graph_title Allocated space for snapshot' echo 'graph_vlabel %' echo 'graph_category disk' - echo 'graph_args --base 100' - ${lvdisplay} -C | awk '$3 ~ /^s/{print $1".label "$1" snapshot of "$5} ' - exit 0 + echo 'graph_args -l 0 -u 100 -r' fi -${lvdisplay} -C | awk '$3 ~ /^s/{print $1".value",int($6)} ' +${lvdisplay} -C | awk '$3 ~ /^s/{print}' | while read line; do + name="$(echo $line | awk '{print $1}')" + id="$(clean_fieldname "$name")" + origin="$(echo $line | awk '{print $5}')" + origin="$(clean_fieldname "$origin")" + percent="$(echo $line | awk '{print $6}')" + + if [ "$1" = "config" ]; then + echo "$id.label $name snapshot of $origin" + else + echo "$id.value $percent" + fi +done diff --git a/plugins/sensors/megaraid-hdd-temperature-using-megacli b/plugins/disk/megaraid-hdd-temperature-using-megacli similarity index 100% rename from plugins/sensors/megaraid-hdd-temperature-using-megacli rename to plugins/disk/megaraid-hdd-temperature-using-megacli diff --git a/plugins/disk/quota2percent_ b/plugins/disk/quota2percent_ new file mode 100755 index 00000000..6b3e6c62 --- /dev/null +++ b/plugins/disk/quota2percent_ @@ -0,0 +1,258 @@ +#!/bin/bash +# -*- sh -*- +: <<=cut + +=head1 NAME + +quota2percent - Plugin to show disk usage in percent of quota hard limit. + +=head1 APPLICABLE SYSTEMS + +All systems with "bash", "quota", "repquota" and "munin" + +Systems with multiple users and individual storage space limitations administered via 'quota' + +=head1 CONFIGURATION + +The following is the default configuration + + [quota2percent_*] + user root + +You could define two alert levels, the graph language, min. human UID and dealing with system users + + [quota2percent_*] + env.warning [value] (default: 90) + env.critical [value] (default: 95) + env.language [en|de|es] (default: en) + env.humanuid [value] (default: 1000, only need if there is an other value define for UID_MIN in /etc/login.defs) + env.low_uid [never|no|yes] (default: never) + set to no for producing rrd files for system user, but don't show those graphs (e.g. for later analyses) + if set to yes system user graphs are drawn + +=head1 DESCRIPTION + +Wild card Plugin for monitoring the utilization of devices with quota rules. +A graph is drawn for each user, which shows the usage as a percentage of his hard limit. System accounts (UID <1000) are suppressed. +In addition, a graph is displayed which indicates the ratio device size to device coverage. +The script repqutoa, usually part of the package quota, is needed. +The plugin itself can be stored in any directory. For example, the device sdb1 shell be monitored, a symbolic link must be created +in the /etc/munin/plugins/ directory as follows: + +=over + +I<<< ln -s //quota2percent_ quota2percent_sdb1 >>> + +=back + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=head1 VERSION + +17.0214 + +=head1 HISTORY + +V17.0214 + + fix hard reading logic operation for skipping by Low_UID=never + fix some slips + fix some nitpicking details + + add env.low_uid + add env.humanid + add env.language + add check if device exist + add if no limitations administered via 'quota' for the device the total line is shown only + add a few comments + add POD documentation + add example graph for Munin Plugin Gallery + + remove setting a PATH + remove German comments + +V17.0124 + + not pubish, first version + +=head1 AUTHOR + +Jo Hartmann + +=head1 LICENSE + +GPLv2 (L) + +=cut + +################################################### +# Autoconf section # +################################################### + + if [ "$1" = "autoconf" ]; then + if ! repquota -V &> /dev/null ; then + echo "no ('requota' executable is missing)" + exit 0 + fi + + if ! df "/dev/${0##*_}" &> /dev/null; then + echo "no (device /dev/${0##*_} does not exist!)" + exit 0 + fi + + echo yes + exit 0 + fi + +################################################### +# Preparation section # +################################################### + +# Load munin's shell libary + . "$MUNIN_LIBDIR/plugins/plugin.sh" + +# if any fetch from munin-node file + Warning=${warning:-90} + Critical=${critical:-95} + Language=${language:-en} + Min_UID=${humanuid:-1000} + Low_UID=${low_uid:-never} + +# Checking if repquota installed and on the path + if ! repquota -V &> /dev/null ; then + echo "The script 'repquota' is not installed or on the path" >&2 + # Send the exit code FATAL ERROR happens + exit 127 + fi + +# get tehe wild card text + Id=${0##*_} + +# Check if device exist + if ! df "/dev/$Id" &> /dev/null; then + echo "The device /dev/$Id does not exist!" >&2 + exit 128 + fi + +################################################### +# Data reading sections # +################################################### + +# Reading the quotes for the selected device, using repquota + if repquota "/dev/$Id" &> /dev/null; then + readarray Quotas < <( repquota "/dev/$Id" | grep " -- " ) + else + echo "No limitations administered via 'quota' for $Id" >&2 + + # If no limitatitons administered: create a dummy for regarding + # the avoidance of a divide-by-zero error + Quotas[0]="root -- 1 1 1 1 1 1" + + # no rrd file need + Low_UID="never" + fi + + readarray Totals < <( df "/dev/$Id" ) + +# Get the count of Users + Users=${#Quotas[@]} + + +################################################### +# Munin Configuration Section # +################################################### + + if [ "$1" = "config" ]; then + + # Localisation of the graphic texts + case $Language in + de) + echo "graph_title Quota-Hard-Limit von $Id" + echo "graph_vlabel Nutzung in % Hardlimit" + echo "graph_info Die Grafik zeigt die Belegung des durch Quota reglementierten Speicherplatzes für alle regulären Nutzer (UID >= $Min_UID) in Prozent des Hardlimits." + Total_txt="Su. aller Nutzer" + Total_info="Inklusive Systemnutzer (UID < $Min_UID)" + ;; + es) + echo "graph_title Cuota de límite absoluto de $Id" + echo "graph_vlabel el % de uso del límite duro" + echo "graph_info El gráfico muestra la disponibilidad de espacio regulado por cuotas para todos los usuarios regulares (UID >= $Min_UID) como porcentaje de límites duros." + Total_txt="Suma de todos los usuarios " + Total_info="La inclusión de usuario del sistema (UID < $Min_UID) " + ;; + *) + echo "graph_title quota hard limit of $Id" + echo "graph_vlabel Usage in %" + echo "graph_info The graphic shows the allocation of the quota-regulated storage space for all regular users (UID >= $Min_UID) as a percentage of the hard limit ." + Total_txt="all users" + Total_info="system users (UID < $Min_UID) included" + ;; + esac + + # Defaults configuration + echo "graph_category disk" + echo "graph_args --lower-limit 0 --upper-limit 100" + echo "graph_printf %5.2lf %%" + echo "graph_scale no" + + # Processing the individual user + for((i=0; i<"$Users"; i++));do + Quota=( ${Quotas[$i]} ) + User=${Quota[0]} + # solve the root problem + Fieldname="$(clean_fieldname "$User")" + + # Determine the currently processing UID + Cur_UID="$(id -u "$User")" + + # skip if actual user a system user und low_uid ist set to never + [ "$Cur_UID" -lt "$Min_UID" ] && [ "$Low_UID" = "never" ] && continue + + # No graph for none human uid if low_uid ist set to no + [ "$Cur_UID" -lt "$Min_UID" ] && echo "$Fieldname.graph $Low_UID" + + # configure the user lines + echo "$Fieldname.label $User" + echo "$Fieldname.warning $Warning" + echo "$Fieldname.critical $Critical" + + done + + # configure the total line and send exit code NO ERROR happens + echo "total.label $Total_txt" + echo "total.warning $Warning" + echo "total.critical $Critical" + echo "total.info $Total_info" + exit 0 + fi + +################################################### +# Munin value section # +################################################### + +# fetch the needed values (used and hard limit) for each user, work around the root problem, calculate the percent value + for((i=0; i<"$Users"; i++));do + Quota=( ${Quotas[$i]} ) + Fieldname="$(clean_fieldname "${Quota[0]}")" + + # skip if actual user a system user und low_uid ist set to never + [ "$Cur_UID" -lt "$Min_UID" ] && [ "$Low_UID" = "never" ] && continue + + # write the result zu munin + echo "${Quota[2]} ${Quota[4]} $Fieldname.value" | awk '{printf "%s %f\n",$3,$1*100/$2}' + + done + +# the value for the total line + Total=( ${Totals[1]} ) + echo "${Total[2]} ${Total[1]} total.value" | awk '{printf "%s %f\n",$3,$1*100/$2}' + +# send the exit code NO ERROR happens + exit 0 + +################################################### +# Script end # +################################################### diff --git a/plugins/disk/raid b/plugins/disk/raid index 120bc366..c835757a 100755 --- a/plugins/disk/raid +++ b/plugins/disk/raid @@ -42,7 +42,7 @@ my($devinfo_re, $devstat_re, $action_re) = ( # Interestingly, swap is presented as "active (auto-read-only)" # and mdadm has '--readonly' option to make the array 'active (read-only)' -my($dev, $ro, $type, $members, $nmem, $nact, $status, $action, $proc, $minute); +my($dev, $ro, $type, $members, $failed, $nmem, $nact, $status, $action, $proc, $minute); while (@text) { my $line = shift @text; if ($line =~ /$devinfo_re/) { @@ -51,6 +51,9 @@ while (@text) { $ro = $2 || ''; $type = $3; $members = $4; + $failed = $members; + $failed =~ s/[^F]+//g; + $failed = length($failed); $line = shift @text; if ($line =~ /$devstat_re/) { @@ -60,7 +63,7 @@ while (@text) { $status = $3; } else { - # sencond line did not exist on /proc/mdstat + # second line did not exist on /proc/mdstat next; } @@ -104,6 +107,9 @@ while (@text) { print $dev, "_rebuild.critical 98:\n"; print $dev, "_check.label $dev check/resync \n"; print $dev, "_check.info $action $minute\n"; + print $dev, "_failed.label $dev failed disks \n"; + print $dev, "_failed.info $action $minute\n"; + print $dev, "_failed.critical 0:0\n"; } else { my $pct = 100 * $nact / $nmem; my $rpct = 100; @@ -127,6 +133,7 @@ while (@text) { print "$dev.value $pct\n"; print $dev, "_rebuild.value $rpct\n"; print $dev, "_check.value $cpct\n"; + print $dev, "_failed.value $failed\n"; } } diff --git a/plugins/disk/raid-mismatch-count b/plugins/disk/raid-mismatch-count index b53dc9c8..e84c1dd1 100755 --- a/plugins/disk/raid-mismatch-count +++ b/plugins/disk/raid-mismatch-count @@ -50,6 +50,8 @@ graph_vlabel Count __EOF__ for target in $targets; do echo "$target.label $target" + echo "$target.critical 1" + echo "$target.info Mismatch count for software raid device $target" done exit fi diff --git a/plugins/snmp/snmp__areca_ b/plugins/disk/snmp__areca_ similarity index 100% rename from plugins/snmp/snmp__areca_ rename to plugins/disk/snmp__areca_ diff --git a/plugins/snmp/snmp__hp_temp b/plugins/disk/snmp__hp_temp similarity index 100% rename from plugins/snmp/snmp__hp_temp rename to plugins/disk/snmp__hp_temp diff --git a/plugins/disk/xfs b/plugins/disk/xfs index 02fc827b..7f32746c 100755 --- a/plugins/disk/xfs +++ b/plugins/disk/xfs @@ -100,7 +100,7 @@ foreach my $func ( keys %config ) { print "graph_title $config{$func}->{title}\n"; print "graph_args --base 1000\n"; print "graph_vlabel $config{$func}->{vtitle}\n"; - print "graph_category filesystem\n"; + print "graph_category disk\n"; my $iter = 0; foreach my $param (@{$config{$func}->{params}}) { print "$param.type DERIVE\n"; diff --git a/plugins/disk/xfs_frag b/plugins/disk/xfs_frag index 8c67ac4c..7218934a 100755 --- a/plugins/disk/xfs_frag +++ b/plugins/disk/xfs_frag @@ -40,7 +40,7 @@ case $1 in cat <<'EOF' graph_title XFS fragmentation graph_vlabel Percent -graph_category filesystem +graph_category disk EOF cat /etc/mtab | awk '{print $2 " " $3}' | while read LINE do diff --git a/plugins/djabberd/djabberd_ b/plugins/djabberd/djabberd_ old mode 100644 new mode 100755 index 46e7227f..14dea8f2 --- a/plugins/djabberd/djabberd_ +++ b/plugins/djabberd/djabberd_ @@ -206,7 +206,7 @@ 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 jabber\n"; + print "graph_category chat\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'}; diff --git a/plugins/mail/dkimproxy_mails b/plugins/dkim/dkimproxy_mails similarity index 97% rename from plugins/mail/dkimproxy_mails rename to plugins/dkim/dkimproxy_mails index e1f2a5b1..8fe7cfe6 100755 --- a/plugins/mail/dkimproxy_mails +++ b/plugins/dkim/dkimproxy_mails @@ -32,7 +32,7 @@ mktemp -t MAIL_LOG=${logfile:-/var/log/mail.log} LOGTAIL=${logtail:-`which logtail`} -STATEFILE=/var/lib/munin/plugin-state/dkimproxy_mails.offset +STATEFILE=$MUNIN_PLUGSTATE/dkimproxy_mails.offset if [ "$1" = "autoconf" ]; then if [ -f "${MAIL_LOG}" -a -n "${LOGTAIL}" -a -x "${LOGTAIL}" ] ; then diff --git a/plugins/docker/docker_cpu b/plugins/docker/docker_cpu index 63165a11..e25b7e95 100755 --- a/plugins/docker/docker_cpu +++ b/plugins/docker/docker_cpu @@ -95,7 +95,7 @@ if (defined $ARGV[0] and $ARGV[0] eq "config") print "graph_vlabel %\n"; print "graph_scale no\n"; print "graph_period second\n"; - print "graph_category Docker\n"; + print "graph_category virtualization\n"; print "graph_info This graph shows docker container CPU usage.\n"; foreach(@result) diff --git a/plugins/docker/docker_memory b/plugins/docker/docker_memory index 1d848043..6e43c0b7 100755 --- a/plugins/docker/docker_memory +++ b/plugins/docker/docker_memory @@ -79,7 +79,7 @@ if (defined $ARGV[0] and $ARGV[0] eq "config") print "graph_title Docker container memory usage\n"; print "graph_args --base 1024 -l 0\n"; print "graph_vlabel Bytes\n"; - print "graph_category Docker\n"; + print "graph_category virtualization\n"; print "graph_info This graph shows docker container memory usage.\n"; foreach(@result) diff --git a/plugins/docker/example_graphs/docker_cpu_usage.png b/plugins/docker/example-graphs/docker_cpu-day.png similarity index 100% rename from plugins/docker/example_graphs/docker_cpu_usage.png rename to plugins/docker/example-graphs/docker_cpu-day.png diff --git a/plugins/docker/example_graphs/docker_memory_usage.png b/plugins/docker/example-graphs/docker_memory-day.png similarity index 100% rename from plugins/docker/example_graphs/docker_memory_usage.png rename to plugins/docker/example-graphs/docker_memory-day.png diff --git a/plugins/dovecot/dovecot b/plugins/dovecot/dovecot old mode 100644 new mode 100755 index c288a44b..303b3ab1 --- a/plugins/dovecot/dovecot +++ b/plugins/dovecot/dovecot @@ -1,235 +1,126 @@ -#!/usr/bin/perl - +#! /bin/bash +# +# Munin Plugin +# to count logins to your dovecot mailserver +# +# Created by Dominik Schulz +# http://developer.gauner.org/munin/ +# Contributions by: +# - Stephane Enten +# - Steve Schnepp +# +# 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 -use Munin::Plugin; +###################### +# Configuration +###################### +EXPR_BIN=/usr/bin/expr +LOGFILE=${logfile:-/var/log/mail.log} +###################### -$pos = undef; -$connected = 0; -$connectedimap = 0; -$connectedpop3 = 0; -$connections = 0; -$connectionsimap = 0; -$connectionspop3 = 0; -$login = 0; -$pop3login = 0; -$imaplogin = 0; -$tls = 0; -$ssl = 0; -$aborted = 0; +if [ "$1" = "autoconf" ]; then + echo yes + exit 0 +fi -($dirname = $0) =~ s/[^\/]+$//; +if [ "$1" = "config" ]; then + echo 'graph_title Dovecot Logins' + echo 'graph_category mail' + echo 'graph_args --base 1000 -l 0' + echo 'graph_vlabel Login Counters' -$logfile = $ENV{'LOGFILE'} || '/var/log/mail.log'; + for t in Total TLS SSL IMAP POP3 + do + field=$(echo $t | tr '[:upper:]' '[:lower:]') + echo "login_$field.label $t Logins" + echo "login_$field.type DERIVE" + echo "login_$field.min 0" + done -# Use an overrided $PATH for all external programs if needed -$DOVEADM = "doveadm"; + echo 'connected.label Connected Users' -if ( $ARGV[0] and $ARGV[0] eq "autoconf" ) { + exit 0 +fi - if (! -x $DOVEADM) { - print "no (no doveadm)\n"; - exit(1); - } - - if (! -f $logfile) { - print "no (logfile $logfile does not exist)\n"; - exit(1); - } - - if (-r "$logfile") { - print "yes\n"; - exit 0; - } else { - print "no (logfile not readable)\n"; - } - exit 1; -} - -if (-f "$logfile.0") { - $rotlogfile = $logfile . ".0"; -} elsif (-f "$logfile.1") { - $rotlogfile = $logfile . ".1"; -} elsif (-f "$logfile.01") { - $rotlogfile = $logfile . ".01"; -} else { - $rotlogfile = $logfile . ".0"; -} - -if ( $ARGV[0] and $ARGV[0] eq "config" ) { - print "multigraph dovecot_connections\n"; - print "graph_title Dovecot connections\n"; - print "graph_args --base 1000 -l 0 --no-gridfit --slope-mode\n"; - print "graph_vlabel connections\n"; - print "graph_category mail\n"; - print "connections.label Connections open\n"; - print "connections.type GAUGE\n"; - print "connections.draw LINE1\n"; - print "connections.min 0\n"; - print "connectionsimap.label IMAP\n"; - print "connectionsimap.type GAUGE\n"; - print "connectionsimap.draw AREA\n"; - print "connectionsimap.min 0\n"; - print "connectionspop3.label POP3\n"; - print "connectionspop3.type GAUGE\n"; - print "connectionspop3.draw STACK\n"; - print "connectionspop3.min 0\n"; - - print "multigraph dovecot_connected\n"; - print "graph_title Dovecot connected users\n"; - print "graph_args --base 1000 -l 0 --no-gridfit --slope-mode\n"; - print "graph_vlabel connections\n"; - print "graph_category mail\n"; - print "connected.label Connected users\n"; - print "connected.type GAUGE\n"; - print "connected.draw LINE1\n"; - print "connected.min 0\n"; - print "connectedimap.label IMAP\n"; - print "connectedimap.type GAUGE\n"; - print "connectedimap.draw AREA\n"; - print "connectedimap.min 0\n"; - print "connectedpop3.label POP3\n"; - print "connectedpop3.type GAUGE\n"; - print "connectedpop3.draw STACK\n"; - print "connectedpop3.min 0\n"; - - print "multigraph dovecot_logins\n"; - print "graph_title Dovecot logins\n"; - print "graph_args --base 1000 -l 0 --no-gridfit --slope-mode\n"; - print "graph_vlabel logins/5 minute\n"; - print "graph_category mail\n"; - print "login.label Logins\n"; - print "login.type GAUGE\n"; - print "login.draw LINE1\n"; - print "login.min 0\n"; - print "imaplogin.label IMAP logins\n"; - print "imaplogin.type GAUGE\n"; - print "imaplogin.draw LINE1\n"; - print "imaplogin.min 0\n"; - print "pop3login.label POP3 logins\n"; - print "pop3login.type GAUGE\n"; - print "pop3login.draw LINE1\n"; - print "pop3login.min 0\n"; - print "tls.label TLS\n"; - print "tls.type GAUGE\n"; - print "tls.draw LINE1\n"; - print "tls.min 0\n"; - print "ssl.label SSL\n"; - print "ssl.type GAUGE\n"; - print "ssl.draw LINE1\n"; - print "ssl.min 0\n"; - print "aborted.label Aborted logins\n"; - print "aborted.type GAUGE\n"; - print "aborted.draw LINE1\n"; - print "aborted.min 0\n"; - exit 0; -} - -if (! -f $logfile and ! -f $rotlogfile) { - print "multigraph dovecot_connections\n"; - print "connections.value U"; - print "connectionsimap.value U"; - print "connectionspop3.value U"; - print "multigraph dovecot_connected\n"; - print "connected.value U\n"; - print "connectedimap.value U\n"; - print "connectedpop3.value U\n"; - print "multigraph dovecot_logins\n"; - print "login.value U\n"; - print "pop3login.value U\n"; - print "imaplogin.value U\n"; - print "tls.value U\n"; - print "ssl.value U\n"; - print "aborted.value U\n"; - - exit 0; -} - -# dit kan beter maar twee calls zijn toch nodig als we niet zelf aggegreren -# suggestie: doveadm who -1 | awk '{print $1" "$2" "$4}' | sort | uniq -c -$connectedimap = `$DOVEADM -f flow who | grep imap | wc -l`; -$connectedpop3 = `$DOVEADM -f flow who | grep pop3 | wc -l`; -$connectionsimap = `$DOVEADM -f flow who -1 | grep imap | wc -l`; -$connectionspop3 = `$DOVEADM -f flow who -1 | grep pop3 | wc -l`; - -#trim -$connectedimap =~ s/\s+$//; -$connectedpop3 =~ s/\s+$//; -$connectionsimap =~ s/\s+$//; -$connectionspop3 =~ s/\s+$//; - -$connected = $connectedimap + $connectedpop3; -$connections = $connectionsimap + $connectionspop3; - -my ($pos) = restore_state(); - -$startsize = (stat $logfile)[7]; - -if (!defined $pos) { - # Initial run. - $pos = $startsize; -} - -if ($startsize < $pos) { - # Log rotated - parseDovecotfile ($rotlogfile, $pos, (stat $rotlogfile)[7]); - $pos = 0; -} - -parseDovecotfile ($logfile, $pos, $startsize); -$pos = $startsize; - -save_state($pos); - -print "multigraph dovecot_connections\n"; -print "connections.value $connections\n"; -print "connectionsimap.value $connectionsimap\n"; -print "connectionspop3.value $connectionspop3\n"; -print "multigraph dovecot_connected\n"; -print "connected.value $connected\n"; -print "connectedimap.value $connectedimap\n"; -print "connectedpop3.value $connectedpop3\n"; -print "multigraph dovecot_logins\n"; -print "login.value $login\n"; -print "pop3login.value $pop3login\n"; -print "imaplogin.value $imaplogin\n"; -print "tls.value $tls\n"; -print "ssl.value $ssl\n"; -print "aborted.value $aborted\n"; - - -sub parseDovecotfile { - my ($fname, $start, $stop) = @_; - open (logf, $fname) or exit 3; - seek (logf, $start, 0) or exit 2; - - while (tell (logf) < $stop) { - my $line =; - chomp ($line); - - if ($line !~ m/dovecot/) { next; } - - if ($line =~ m/Aborted/) { - $aborted++; - - } elsif ($line =~ m/Login:/) { - $login++; - - if ( $line =~ m/TLS/) { - $tls++; - } elsif ($line =~ m/SSL/) { - $ssl++; - } - - if ( $line =~ m/pop3-login:/) { - $pop3login++; - } elsif ($line =~ m/imap-login:/) { - $imaplogin++; - } - } - } - close(logf); -} - -# vim:syntax=perl +###################### +# Total Logins +###################### +echo -en "login_total.value " +VALUE=$(egrep -c '[dovecot]?.*Login' $LOGFILE) +if [ ! -z "$VALUE" ]; then + echo "$VALUE" +else + echo "0" +fi +echo -n +###################### +# Connected Users +###################### +DISCONNECTS=$(egrep -c '[dovecot]?.*Disconnected' $LOGFILE) +CONNECTS=$(egrep -c '[dovecot]?.*Login' $LOGFILE) +VALUE=$($EXPR_BIN $CONNECTS - $DISCONNECTS) +if [ -z "$VALUE" ] || [ "$VALUE" -lt 0 ]; then + VALUE=0 +fi +echo -en "connected.value " +echo $VALUE +echo -n +###################### +# TLS Logins +###################### +echo -en "login_tls.value " +VALUE=$(egrep -c '[dovecot]?.*Login.*TLS' $LOGFILE) +if [ ! -z "$VALUE" ]; then + echo "$VALUE" +else + echo "0" +fi +echo -n +###################### +# SSL Logins +###################### +echo -en "login_ssl.value " +VALUE=$(egrep -c '[dovecot]?.*Login.*SSL' $LOGFILE) +if [ ! -z "$VALUE" ]; then + echo "$VALUE" +else + echo "0" +fi +echo -n +###################### +# IMAP Logins +###################### +echo -en "login_imap.value " +VALUE=$(egrep -c '[dovecot]?.*imap.*Login' $LOGFILE) +if [ ! -z "$VALUE" ]; then + echo "$VALUE" +else + echo "0" +fi +echo -n +###################### +# POP3 Logins +###################### +echo -en "login_pop3.value " +VALUE=$(egrep -c '[dovecot]?.*pop3.*Login' $LOGFILE) +if [ ! -z "$VALUE" ]; then + echo "$VALUE" +else + echo "0" +fi +echo -n diff --git a/plugins/dovecot/dovecot1 b/plugins/dovecot/dovecot1 new file mode 100755 index 00000000..af73b414 --- /dev/null +++ b/plugins/dovecot/dovecot1 @@ -0,0 +1,242 @@ +#!/usr/bin/perl + +#%# family=auto +#%# capabilities=autoconf + +use Munin::Plugin; + +$pos = undef; +$connected = 0; +$connectedimap = 0; +$connectedpop3 = 0; +$connections = 0; +$connectionsimap = 0; +$connectionspop3 = 0; +$login = 0; +$pop3login = 0; +$imaplogin = 0; +$tls = 0; +$ssl = 0; +$aborted = 0; + +($dirname = $0) =~ s/[^\/]+$//; + +$dovelogfile = 0 ; + +$logfile = $ENV{'LOGFILE'} || '/var/log/mail.log'; + +if ( $logfile =~ /dovecot/ ) { + $dovelogfile = 1 ; +} + +# Use an overrided $PATH for all external programs if needed +$DOVEADM = "doveadm"; + +if ( $ARGV[0] and $ARGV[0] eq "autoconf" ) { + + if (! -x $DOVEADM) { + print "no (no doveadm)\n"; + exit(1); + } + + if (! -f $logfile) { + print "no (logfile $logfile does not exist)\n"; + exit(1); + } + + if (-r "$logfile") { + print "yes\n"; + exit 0; + } else { + print "no (logfile not readable)\n"; + } + exit 1; +} + +if (-f "$logfile.0") { + $rotlogfile = $logfile . ".0"; +} elsif (-f "$logfile.1") { + $rotlogfile = $logfile . ".1"; +} elsif (-f "$logfile.01") { + $rotlogfile = $logfile . ".01"; +} else { + $rotlogfile = $logfile . ".0"; +} + +if ( $ARGV[0] and $ARGV[0] eq "config" ) { + print "multigraph dovecot_connections\n"; + print "graph_title Dovecot connections\n"; + print "graph_args --base 1000 -l 0 --no-gridfit --slope-mode\n"; + print "graph_vlabel connections\n"; + print "graph_category mail\n"; + print "connections.label Connections open\n"; + print "connections.type GAUGE\n"; + print "connections.draw LINE1\n"; + print "connections.min 0\n"; + print "connectionsimap.label IMAP\n"; + print "connectionsimap.type GAUGE\n"; + print "connectionsimap.draw AREA\n"; + print "connectionsimap.min 0\n"; + print "connectionspop3.label POP3\n"; + print "connectionspop3.type GAUGE\n"; + print "connectionspop3.draw STACK\n"; + print "connectionspop3.min 0\n"; + + print "multigraph dovecot_connected\n"; + print "graph_title Dovecot connected users\n"; + print "graph_args --base 1000 -l 0 --no-gridfit --slope-mode\n"; + print "graph_vlabel connections\n"; + print "graph_category mail\n"; + print "connected.label Connected users\n"; + print "connected.type GAUGE\n"; + print "connected.draw LINE1\n"; + print "connected.min 0\n"; + print "connectedimap.label IMAP\n"; + print "connectedimap.type GAUGE\n"; + print "connectedimap.draw AREA\n"; + print "connectedimap.min 0\n"; + print "connectedpop3.label POP3\n"; + print "connectedpop3.type GAUGE\n"; + print "connectedpop3.draw STACK\n"; + print "connectedpop3.min 0\n"; + + print "multigraph dovecot_logins\n"; + print "graph_title Dovecot logins\n"; + print "graph_args --base 1000 -l 0 --no-gridfit --slope-mode\n"; + print "graph_vlabel logins/5 minute\n"; + print "graph_category mail\n"; + print "login.label Logins\n"; + print "login.type GAUGE\n"; + print "login.draw LINE1\n"; + print "login.min 0\n"; + print "imaplogin.label IMAP logins\n"; + print "imaplogin.type GAUGE\n"; + print "imaplogin.draw LINE1\n"; + print "imaplogin.min 0\n"; + print "pop3login.label POP3 logins\n"; + print "pop3login.type GAUGE\n"; + print "pop3login.draw LINE1\n"; + print "pop3login.min 0\n"; + print "tls.label TLS\n"; + print "tls.type GAUGE\n"; + print "tls.draw LINE1\n"; + print "tls.min 0\n"; + print "ssl.label SSL\n"; + print "ssl.type GAUGE\n"; + print "ssl.draw LINE1\n"; + print "ssl.min 0\n"; + print "aborted.label Aborted logins\n"; + print "aborted.type GAUGE\n"; + print "aborted.draw LINE1\n"; + print "aborted.min 0\n"; + exit 0; +} + +if (! -f $logfile and ! -f $rotlogfile) { + print "multigraph dovecot_connections\n"; + print "connections.value U"; + print "connectionsimap.value U"; + print "connectionspop3.value U"; + print "multigraph dovecot_connected\n"; + print "connected.value U\n"; + print "connectedimap.value U\n"; + print "connectedpop3.value U\n"; + print "multigraph dovecot_logins\n"; + print "login.value U\n"; + print "pop3login.value U\n"; + print "imaplogin.value U\n"; + print "tls.value U\n"; + print "ssl.value U\n"; + print "aborted.value U\n"; + + exit 0; +} + +# dit kan beter maar twee calls zijn toch nodig als we niet zelf aggegreren +# suggestie: doveadm who -1 | awk '{print $1" "$2" "$4}' | sort | uniq -c +$connectedimap = `$DOVEADM -f flow who | grep imap | wc -l`; +$connectedpop3 = `$DOVEADM -f flow who | grep pop3 | wc -l`; +$connectionsimap = `$DOVEADM -f flow who -1 | grep imap | wc -l`; +$connectionspop3 = `$DOVEADM -f flow who -1 | grep pop3 | wc -l`; + +#trim +$connectedimap =~ s/\s+$//; +$connectedpop3 =~ s/\s+$//; +$connectionsimap =~ s/\s+$//; +$connectionspop3 =~ s/\s+$//; + +$connected = $connectedimap + $connectedpop3; +$connections = $connectionsimap + $connectionspop3; + +my ($pos) = restore_state(); + +$startsize = (stat $logfile)[7]; + +if (!defined $pos) { + # Initial run. + $pos = $startsize; +} + +if ($startsize < $pos) { + # Log rotated + parseDovecotfile ($rotlogfile, $pos, (stat $rotlogfile)[7]); + $pos = 0; +} + +parseDovecotfile ($logfile, $pos, $startsize); +$pos = $startsize; + +save_state($pos); + +print "multigraph dovecot_connections\n"; +print "connections.value $connections\n"; +print "connectionsimap.value $connectionsimap\n"; +print "connectionspop3.value $connectionspop3\n"; +print "multigraph dovecot_connected\n"; +print "connected.value $connected\n"; +print "connectedimap.value $connectedimap\n"; +print "connectedpop3.value $connectedpop3\n"; +print "multigraph dovecot_logins\n"; +print "login.value $login\n"; +print "pop3login.value $pop3login\n"; +print "imaplogin.value $imaplogin\n"; +print "tls.value $tls\n"; +print "ssl.value $ssl\n"; +print "aborted.value $aborted\n"; + + +sub parseDovecotfile { + my ($fname, $start, $stop) = @_; + open (logf, $fname) or exit 3; + seek (logf, $start, 0) or exit 2; + + while (tell (logf) < $stop) { + my $line =; + chomp ($line); + + if ( $dovelogfile == 0 and $line !~ m/dovecot/) { next; } + else { + if ($line =~ m/Aborted/) { + $aborted++; + + } elsif ($line =~ m/Login:/) { + $login++; + + if ( $line =~ m/TLS/) { + $tls++; + } elsif ($line =~ m/SSL/) { + $ssl++; + } + + if ( $line =~ m/pop3-login:/) { + $pop3login++; + } elsif ($line =~ m/imap-login:/) { + $imaplogin++; + } + } + } + } + close(logf); +} + +# vim:syntax=perl diff --git a/plugins/mail/dovecot_stats_ b/plugins/dovecot/dovecot_stats_ old mode 100644 new mode 100755 similarity index 100% rename from plugins/mail/dovecot_stats_ rename to plugins/dovecot/dovecot_stats_ diff --git a/plugins/drbd/drbd b/plugins/drbd/drbd index 63c5a02b..4216902e 100755 --- a/plugins/drbd/drbd +++ b/plugins/drbd/drbd @@ -14,7 +14,7 @@ my $temp; sub display{ if ($ARGV[0] and $ARGV[0] eq "config"){ print "graph_title DRBD\n"; - print "graph_category DRBD\n"; + print "graph_category disk\n"; print "graph_info Graph DRBD\n"; print "graph_vlabel Bytes per \${graph_period} read (-) / written (+)\n"; print "graph_args --base 1024 --lower-limit 0\n"; diff --git a/plugins/drbd/drbd-stat b/plugins/drbd/drbd-stat index 58d91c9f..2a6e7259 100755 --- a/plugins/drbd/drbd-stat +++ b/plugins/drbd/drbd-stat @@ -15,7 +15,7 @@ my $temp; sub display{ if ($ARGV[0] and $ARGV[0] eq "config"){ print "graph_title DRBD\n"; - print "graph_category DRBD\n"; + print "graph_category disk\n"; print "graph_info Graph DRBD\n"; print "graph_vlabel Graph DRBD\n"; #print "graph_scale no\n"; diff --git a/plugins/drupal/drupal_files_total.php b/plugins/drupal/drupal_files_total.php index 0cfe9604..dc8edecc 100755 --- a/plugins/drupal/drupal_files_total.php +++ b/plugins/drupal/drupal_files_total.php @@ -45,7 +45,7 @@ if (count($argv) === 2 && $argv[1] === 'config') { echo "graph_title Drupal Total Files\n"; echo "graph_args --base 1000 --lower-limit 0\n"; echo "graph_vlabel Files Count / ${graph_period}\n"; - echo "graph_category Drupal\n"; + echo "graph_category cms\n"; echo "graph_scale no\n"; echo "graph_info Displays the online files uploaded to your Drupal site\n"; diff --git a/plugins/drupal/drupal_forums_total.php b/plugins/drupal/drupal_forums_total.php index 6a73d2ee..fa221c30 100755 --- a/plugins/drupal/drupal_forums_total.php +++ b/plugins/drupal/drupal_forums_total.php @@ -45,7 +45,7 @@ if (count($argv) === 2 && $argv[1] === 'config') { echo "graph_title Drupal Total Forums\n"; echo "graph_args --base 1000 --lower-limit 0\n"; echo "graph_vlabel Total Forums and comments Count / ${graph_period}\n"; - echo "graph_category Drupal\n"; + echo "graph_category cms\n"; echo "graph_scale no\n"; echo "graph_info Displays the sum of forums and comments made in your Drupal site\n"; diff --git a/plugins/drupal/drupal_node_distribution_count.php b/plugins/drupal/drupal_node_distribution_count.php index 2e282e2d..1c4e52db 100755 --- a/plugins/drupal/drupal_node_distribution_count.php +++ b/plugins/drupal/drupal_node_distribution_count.php @@ -55,7 +55,7 @@ if (count($argv) === 2 && $argv[1] === 'config') { echo "graph_title Drupal Node Distribution Count\n"; echo "graph_args --base 1000 --lower-limit 0\n"; echo "graph_vlabel Node Distribution Count / ${graph_period}\n"; - echo "graph_category Drupal\n"; + echo "graph_category cms\n"; echo "graph_scale nol\n"; echo "graph_info Displays the nodes content type distribution count in your Drupal site\n"; diff --git a/plugins/drupal/drupal_page_speed_test.php b/plugins/drupal/drupal_page_speed_test.php old mode 100644 new mode 100755 index 2041d3a8..c89ad3d9 --- a/plugins/drupal/drupal_page_speed_test.php +++ b/plugins/drupal/drupal_page_speed_test.php @@ -65,7 +65,7 @@ if (count($argv) === 2 && $argv[1] === 'config') { echo "graph_title Drupal Page Speed Test\n"; echo "graph_args --base 1000 --lower-limit 0\n"; echo "graph_vlabel Page Load Time / ${graph_period}\n"; - echo "graph_category Drupal\n"; + echo "graph_category cms\n"; echo "graph_scale no\n"; echo "graph_info Displays the time it took for several pages to load\n"; diff --git a/plugins/drupal/drupal_users_online.php b/plugins/drupal/drupal_users_online.php index b682f163..72e2faf4 100755 --- a/plugins/drupal/drupal_users_online.php +++ b/plugins/drupal/drupal_users_online.php @@ -45,7 +45,7 @@ if (count($argv) === 2 && $argv[1] === 'config') { echo "graph_title Drupal Online Users\n"; echo "graph_args --base 1000 --lower-limit 0\n"; echo "graph_vlabel Online Users Count / ${graph_period}\n"; - echo "graph_category Drupal\n"; + echo "graph_category cms\n"; echo "graph_scale no\n"; echo "graph_info Displays the online users, members and anonymous, in your Drupal site\n"; diff --git a/plugins/drupal/drupal_users_total.php b/plugins/drupal/drupal_users_total.php index 0521b419..9cf29929 100755 --- a/plugins/drupal/drupal_users_total.php +++ b/plugins/drupal/drupal_users_total.php @@ -45,7 +45,7 @@ if (count($argv) === 2 && $argv[1] === 'config') { echo "graph_title Drupal Total Users\n"; echo "graph_args --base 1000 --lower-limit 0\n"; echo "graph_vlabel Total Users Count / ${graph_period}\n"; - echo "graph_category Drupal\n"; + echo "graph_category cms\n"; echo "graph_scale no\n"; echo "graph_info Displays the sum of users, as well as disabled count, in your Drupal site\n"; diff --git a/plugins/dspam/dspam_ b/plugins/dspam/dspam_ index 5f95927e..78e0f390 100755 --- a/plugins/dspam/dspam_ +++ b/plugins/dspam/dspam_ @@ -61,7 +61,7 @@ EXAMPLES and USAGE for details). Link this plugin to /etc/munin/plugins/ and restart the munin-node. The link should be in the format: dspam__, where: - graph - One of: accuracy, processed, processed_abs. + graph - One of: accuracy, processed, absprocessed, relprocessed. target - The uid that DSPAM generates in dspam_stats output, but converted to Munin internal name format. Normally this means that non-alphabetic and non-numeral characters @@ -83,12 +83,16 @@ The plugin supports the following graph types: correctly classified messages (both ham and spam) in relation to the number of all processed messages. - absprocessed - Shows the absolute numbers of mesages processed, + absprocessed - Shows the absolute numbers of messages processed, sorted by the classification that DSPAM uses. The numbers are stacked, making the height of the column display the increase of processed messages over time. - processed - Shows the same data as dspam_processed_abs_, but as + relprocessed - Shows the same data as dspam_absprocessed_, but as + messages per minute instead of ever-growing asolute + values. + + processed - Shows the same data as dspam_absprocessed_, but as percentage of the total amount of processed messages, making it clear to see how the amounts of classified messages are divided. @@ -211,7 +215,16 @@ file_is_locked() { local file=$1 local lock=$1.lock - [ -f $lock ] && debug file $file is locked && return 0 # EX_OK + if [ -f "$lock" ];then + debug file $file is locked + local pid=$(cat "$lock") + if ps h -p "$pid" -o comm=|grep -q dspam_; then + return 0 # EX_OK + else + debug lock for file $file is no longer valid + file_remove_lock $file + fi + fi debug file $file is not locked return 69 # EX_UNAVAILABLE } @@ -234,23 +247,22 @@ update_statefile() { debug starting $dspam_stats -t -S local t_start=$(date +%s) - $dspam_stats -t -S | while read a b c d e f g h i j k l m x; do - + $dspam_stats -t -S | while IFS=' :' read a b c d e f g h i j k l m x; do # example of output format (3.9.1 rc1) for each user: #username@example.org # TP: 0 TN: 2147 FP: 0 FN: 53 SC: 0 NC: 0 # SHR: 0.00% HSR: 0.00% OCA: 97.59% # or for short user names: - #vmail TP: 1141 TN: 459 FP: 0 FN: 5 SC: 0 NC: 0 + #vmail TP:80312082 TN:198342928 FP: 82941 FN: 57326 SC: 0 NC: 3498 # SHR: 99.56% HSR: 0.00% OCA: 99.69% case $a in - TP:) + TP) # the 2nd line local tp=$b tn=$d fp=$f fn=$h sc=$j nc=$l ;; - SHR:) + SHR) # the 3rd line local shr=$(echo $b | sed 's/%$//g') local hsr=$(echo $d | sed 's/%$//g') @@ -267,7 +279,7 @@ update_statefile() { # the 1st line local uid=$a # data from 2nd line is also here - [ "$b" = "TP:" ] && local tp=$c tn=$e fp=$g fn=$i sc=$k nc=$m + [ "$b" = "TP" ] && local tp=$c tn=$e fp=$g fn=$i sc=$k nc=$m ;; esac done @@ -332,6 +344,7 @@ print_suggest() { echo accuracy_ALL echo processed_ALL echo absprocessed_ALL + echo relprocessed_ALL } # @@ -368,7 +381,7 @@ print_config() { fi echo "graph_title Accuracy for $uid" - echo graph_category dspam + echo graph_category spamfilter echo graph_args --base 1000 --upper-limit 100 --rigid echo graph_vlabel Accuracy in % echo "graph_info This graph shows the current DSPAM Overall Accuracy for $uid ($uid_count uids). Overall Accuracy is the percentage of messages that is classified correctly as either ham or spam." @@ -406,7 +419,7 @@ print_config() { fi echo "graph_title Processed messages for $uid (%)" - echo graph_category dspam + echo graph_category spamfilter echo graph_args --base 1000 --upper-limit 100 --rigid echo graph_vlabel Messages in % echo "graph_info This graph shows the messages that DSPAM processed for $uid ($uid_count uids) in percentages of all processed messages. Messages are divided in the following categories: true positives/negatives, false positives/negatives, and corpusfed ham/spam." @@ -430,7 +443,7 @@ print_config() { echo nc.draw AREASTACK ;; - absprocessed) + *processed) if [ -n "$pattern" ]; then debug env.pattern was set, so use it: $pattern local uid=$description @@ -451,28 +464,36 @@ print_config() { fi echo "graph_title Processed messages for $uid" - echo graph_category dspam + echo graph_category spamfilter echo graph_args --base 1000 - echo graph_vlabel Messages + [ "$graph" = absprocessed ] && echo graph_vlabel Messages + [ "$graph" = relprocessed ] && echo graph_vlabel Messages / minute + [ "$graph" = relprocessed ] && echo graph_period minute echo graph_total Total echo "graph_info This graph shows the messages that DSPAM processed for $uid ($uid_count uids). Messages are divided in the following categories: true positives/negatives, false positives/negatives, and corpusfed ham/spam." echo tp.label True positives echo tp.info Spam messages correctly classified as spam. + [ "$graph" = relprocessed ] && echo tp.type DERIVE echo tp.draw AREASTACK echo tn.label True negatives echo tn.info Ham messages correctly classified as ham. + [ "$graph" = relprocessed ] && echo tn.type DERIVE echo tn.draw AREASTACK echo fp.label False positives echo fp.info Ham messages incorrectly classified as spam, but corrected by the user. + [ "$graph" = relprocessed ] && echo fp.type DERIVE echo fp.draw AREASTACK echo fn.label False negatives echo fn.info Spam messages incorrectly classified as ham, but corrected by the user. + [ "$graph" = relprocessed ] && echo fn.type DERIVE echo fn.draw AREASTACK echo sc.label Corpusfed spam echo sc.info Spam messages from a collected corpus for training purposes. + [ "$graph" = relprocessed ] && echo sc.type DERIVE echo sc.draw AREASTACK echo nc.label Corpusfed ham echo nc.info Ham messages from a collected corpus for training purposes. + [ "$graph" = relprocessed ] && echo nc.type DERIVE echo nc.draw AREASTACK ;; @@ -561,7 +582,7 @@ print_fetch() { echo nc.value $(abs2perc $all_nc $total) ;; - absprocessed) + *processed) if [ -n "$pattern" ]; then debug env.pattern was set, so use it: $pattern continue diff --git a/plugins/dspam/dspam_activity b/plugins/dspam/dspam_activity index 39ffd5bf..b4e1f952 100755 --- a/plugins/dspam/dspam_activity +++ b/plugins/dspam/dspam_activity @@ -169,7 +169,7 @@ print_config() { debug printing config echo "graph_title DSPAM activity" - echo graph_category dspam + echo graph_category spamfilter echo graph_args --base 1000 echo graph_vlabel Messages / \${graph_period} echo graph_period minute diff --git a/plugins/dvb/2wcomdsr_ b/plugins/dvb/2wcomdsr_ old mode 100644 new mode 100755 index 5c73375a..86c43265 --- a/plugins/dvb/2wcomdsr_ +++ b/plugins/dvb/2wcomdsr_ @@ -62,7 +62,7 @@ echo "host_name $hostname" echo "graph_args --base 1000" echo "graph_title Carrier/Noise Ratio" echo "graph_vlabel dB" -echo "graph_category dvb" +echo "graph_category radio" echo "graph_scale no" echo "graph_info Calculated carrier/noise ratio of the signal. To avoid data errors, the decoding of a received signal requires a minimum C/N ratio that depends on the FEC rate." echo "cpn.label C/N" @@ -74,7 +74,7 @@ echo "host_name $hostname" echo "graph_args --base 1000" echo "graph_title Carrier Frequency Offset" echo "graph_vlabel MHz" -echo "graph_category dvb" +echo "graph_category radio" echo "graph_scale no" echo "graph_info Incoming signals from the LNB are subject to frequency tolerances, thus check the 'CFO Error' under menu item 'Status>Tuner'. This value represents the margin by which the entered frequency deviates from the actual frequency. In order to maximize reception quality the CFO error should be minimized." echo "cfo.label CFO" @@ -86,7 +86,7 @@ echo "host_name $hostname" echo "graph_args --base 1000" echo "graph_title RF Signal Level" echo "graph_vlabel dBm" -echo "graph_category dvb" +echo "graph_category radio" echo "graph_scale no" echo "graph_info Input for the connection of the receiving LNB of the satellite antenna. Admissible RF signal input range is -82 to -10 dBm." echo "inp.label RF level" @@ -98,7 +98,7 @@ echo "host_name $hostname" echo "graph_args --base 1000" echo "graph_title Tuner system Status" echo "graph_vlabel 1=OK, 0=BAD" -echo "graph_category dvb" +echo "graph_category radio" echo "graph_scale no" echo "graph_info Tuner system status: 1 = 'OK' or 0 = 'BAD'. It is OK if all the following values are OK: AGC (Automatic Gain Control), CE (Carrier Frequency Offset Estimation), Symbol Timing, Carrier Locked, Viterbi error correction." echo "tnr.label Tuner status" @@ -110,7 +110,7 @@ echo "host_name $hostname" echo "graph_args --base 1000" echo "graph_title Warning LED" echo "graph_vlabel 0=OFF; >0=ON" -echo "graph_category dvb" +echo "graph_category radio" echo "graph_scale no" echo "graph_info Every monitoring function has a checkbox 'warning LED on' to select if the 'Warning' LED on the web interface and the front panel of the device is activated on alarms or not." echo "ld2.label Warning" @@ -122,7 +122,7 @@ echo "host_name $hostname" echo "graph_args --base 1000 --logarithmic" echo "graph_title BER Viterbi/LDPC" echo "graph_vlabel Bit Error Rate" -echo "graph_category dvb" +echo "graph_category radio" #echo "graph_scale no" echo "graph_info Detected bit error rate (BER) before the Viterbi error correction." echo "ber_vt.label Viterbi/LDPC BER" @@ -134,7 +134,7 @@ echo "host_name $hostname" echo "graph_args --base 1000 --logarithmic" echo "graph_title BER Reed Solomon/BCH" echo "graph_vlabel Bit Error Rate" -echo "graph_category dvb" +echo "graph_category radio" #echo "graph_scale no" echo "graph_info Detected bit error rate (BER) before the Reed Solomon error correction." echo "ber_rs.label RS/BCH BER" diff --git a/plugins/dvb/femon b/plugins/dvb/femon index 8d6ba206..b51a17fb 100755 --- a/plugins/dvb/femon +++ b/plugins/dvb/femon @@ -14,7 +14,7 @@ # # Parameters # femonpath - Specify path to femon program (Default: /usr/bin/femon) -# graph_args - Specifiy graph args (Default: --lower-limit 0 --upper-limit 100 --rigid) +# graph_args - Specify graph args (Default: --lower-limit 0 --upper-limit 100 --rigid) # # Author: Nicolas Knotzer # @@ -93,7 +93,7 @@ def print_adapter_config(dvb_adapter) : print ('graph_title DVB Femon Sensors '+dvb_adapter[0]) print ('graph_args '+os.getenv('graph_args','--lower-limit 0 --upper-limit 100 --rigid')) print ('graph_vlabel Quality') - print ('graph_category DVB') + print ('graph_category tv') print ('graph_info This graph shows femon output for your dvb-'+dvb_adapter[0]) print ('str.label Signal Strength') diff --git a/plugins/ejabberd/ejabberd_resources_/ejabberd_resources_ b/plugins/ejabberd/ejabberd_resources_ similarity index 96% rename from plugins/ejabberd/ejabberd_resources_/ejabberd_resources_ rename to plugins/ejabberd/ejabberd_resources_ index 4621a5e7..d820b2de 100755 --- a/plugins/ejabberd/ejabberd_resources_/ejabberd_resources_ +++ b/plugins/ejabberd/ejabberd_resources_ @@ -1,7 +1,5 @@ #!/usr/bin/env bash -# ejabberd_resources_ revision 4 (Mar 2015) -# # Tested with ejabberd 2.1.x # # This plugin is capable to show: @@ -206,6 +204,20 @@ function ejabberd_report_mnesia_recs() { ejabberd_report_mnesia "$1" recs } +function ejabberd_report_process_links() { + ejabberd_exec " + lists:filtermap( + fun(Name) -> + case whereis(Name) of + Pid when is_pid(Pid) -> + {links, Links} = erlang:process_info(Pid, links), + {true, {Name, length(Links)}}; + _ -> + false + end, + registered())" +} + function open_files_counter_util() { if hash lsof &>/dev/null; then echo lsof @@ -281,6 +293,7 @@ case $1 in memory processes ports +process_links online_users registered_users mnesia_recs @@ -293,7 +306,7 @@ SUGGESTIONS cat < + +=head1 LICENSE + + GPLv2 + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=head1 DESCRIPTION + + The plugin monitors LUN of EMC Unified Storage FLARE SP's. Probably it can also + be compatible with other Clariion systems. It uses SSH to connect to Control + Stations, then remotely executes /nas/sbin/navicli and fetches and parses data + from it. Obviously, it's easy to reconfigure plugin not to use Control Stations' + navicli in favor of using locally installed /opt/Navisphere's cli. There is no + difference which Storage Processor to use to gather data, so this plugin tries + both of them and uses the first active one. This plugin also automatically + chooses Primary Control Station from the list by calling /nasmcd/sbin/getreason + and /nasmcd/sbin/t2slot. + + I left some parts of this plugin as rudimental to make easy to reconfigure it + to draw more (or less) data. + + The plugin has been tested in the following Operating Environment (OE): + File Version T7.1.76.4 + Block Revision 05.32.000.5.215 + +=head1 COMPATIBILITY + + The plugin has been written for being compatible with EMC VNX5300 Storage + system, as this is the only EMC storage which i have. By the way, i am pretty + sure it can also work with other VNX1 storages, like VNX5100 and VNX5500, and + old-style Clariion systems. + About VNX2 series, i don't know whether the plugin will be able to work with + them. Maybe it would need some corrections in command-line backend. The same + situation is with other EMC systems, so i encourage you to try and fix the + plugin. + +=head1 LIST OF GRAPHS + + Graph category Disk: + EMC VNX 5300 LUN Blocks + EMC VNX 5300 LUN Requests + EMC VNX 5300 Counted Load per LUN + EMC VNX 5300 Sum of Outstanding Requests + EMC VNX 5300 Non-Zero Request Count Arrivals + EMC VNX 5300 Trespasses + EMC VNX 5300 Counted Block Queue Length + EMC VNX 5300 Counted Load per SP + + +=head1 CONFIGURATION + +=head2 Prerequisites + + First of all, be sure that statistics collection is turned on. You can do this + by typing: + navicli -h spa setstats -on + on your Control Station or locally through /opt/Navisphere + + Also, the plugin actively uses buggy "cdef" feature of Munin 2.0, and here we + can be hit by the following bugs: + http://munin-monitoring.org/ticket/1017 - Here I have some workarounds in the + plugin, be sure that they are working. + http://munin-monitoring.org/ticket/1352 - Metrics in my plugin can be much + longer than 15 characters. + Without these workarounds "Load" and "Queue Length" would not work. + +=head2 Installation + + The plugin uses SSH to connect to Control Stations. It's possible to use + 'nasadmin' user, but it would be better if you create read-only global user by + Unisphere Client. The user should have only Operator role. + I created "operator" user but due to the fact that Control Stations already + had one internal "operator" user, the new one was called "operator1". So be + careful. After that, copy .bash_profile from /home/nasadmin to a newly created + /home/operator1. + + On munin-node side choose a user which will be used to connect through SSH. + Generally user "munin" is ok. Then, execute "sudo su munin -s /bin/bash", + "ssh-keygen" and "ssh-copy-id" to both Control Stations with newly created + user. + + Make a link from /usr/share/munin/plugins/emc_vnx_dm_basic_stats to + /etc/munin/plugins/emc_vnx_dm_basic_stats_, where is any + arbitrary name of your storage system. The plugin will return in its + answer as "host_name" field. + + For example, assume your storage system is called "VNX5300". + Make a configuration file at + /etc/munin/plugin-conf.d/emc_vnx_block_lun_perfdata_VNX5300. For example: + + [emc_vnx_block_lun_perfdata_VNX5300] + user munin + env.username operator1 + env.cs_addr 192.168.1.1 192.168.1.2 + + or: + + [emc_vnx_block_lun_perfdata_VNX5300] + user munin + env.username operator1 + env.localcli /opt/Navisphere/bin/naviseccli + env.sp_addr 192.168.0.3 192.168.0.4 + env.blockpw foobar + + Where: + user - SSH Client local user + env.username - Remote user with Operator role for Block or File part + env.cs_addr - Control Stations addresses for remote (indirect) access. + env.localcli - Optional. Path of localhost 'Naviseccli' binary. If this + variable is set, env.cs_addr is ignored, and local 'navicli' is used. + Requires env.blockpw variable. + env.sp_addr - Default is "SPA SPB". In case of "direct" connection to + Storage Processors, their addresses/hostnames are written here. + env.blockpw - Password for connecting to Storage Processors + +=head1 ERRATA + + It counts Queue Length in not fully correct way. We take parameters totally + from both SP's, but after we divide them independently by load of SPA and SPB. + Anyway, in most AAA / ALUA cases the formula is correct. + +=head1 HISTORY + + 09.11.2016 - First Release + 26.12.2016 - Compatibility with Munin coding style + +=cut + +export LANG=C + +. "$MUNIN_LIBDIR/plugins/plugin.sh" + +cs_addr="${cs_addr:=""}" +username="${username:=""}" +blockpw="${blockpw:=""}" + +TARGET=$(echo "${0##*/}" | cut -d _ -f 6) + +# "All Storage Processors we have" +if [[ -v "sp_addr" ]]; then + SPALL=$sp_addr +else + SPALL="SPA SPB" +fi +# "navicli" command. Can be local or remote, through Control Stations +if [[ -v "localcli" ]]; then + NAVICLI=$localcli +else + NAVICLI="/nas/sbin/navicli" +fi + +# Prints "10" on stdout if found Primary Online control station. "11" - for Secondary Online control station. +ssh_check_cmd() { + ssh -q "$username@$1" "/nasmcd/sbin/getreason | grep -w \"slot_\$(/nasmcd/sbin/t2slot)\" | cut -d- -f1 | awk '{print \$1}' " +} + + +check_conf_and_set_vars () { + if [ -z "$username" ]; then + echo "No username ('username' environment variable)!" + return 1 + fi + + if [ -z "$localcli" ]; then + if [ -z "$cs_addr" ]; then + echo "No control station addresses ('cs_addr' environment variable)!" + return 1 + fi + #Choosing Cotrol Station. Code have to be "10" + for CS in $cs_addr; do + if [[ "10" = "$(ssh_check_cmd "$CS")" ]]; then + PRIMARY_CS=$CS + SSH="ssh -q $username@$PRIMARY_CS " + break + fi + done + + if [ -z "$PRIMARY_CS" ]; then + echo "No alive primary Control Station from list \"$cs_addr\""; + return 1 + fi + else + if [ ! -f "$localcli" ]; then + echo "Local CLI is set, but no binary found at $localcli!" + return 1 + fi + if [ -z "$blockpw" ]; then + echo "No Password for Block Access ('blockpw' environment variable)!" + return 1 + fi + SSH="" + NAVICLI="$localcli -User $username -Password $blockpw -Scope 0 " + fi + local probe_sp + for probe_sp in $SPALL; do + # shellcheck disable=SC2086 + if $SSH $NAVICLI -h "$probe_sp" >/dev/null 2>&1; then + StorageProcessor="$probe_sp" + break + fi + done + [ -z "$StorageProcessor" ] && echo "No active Storage Processor found!" && return 1 + NAVICLI_NOSP="$NAVICLI -h" + NAVICLI="$NAVICLI -h $StorageProcessor" + return 0 +} + +if [ "$1" = "autoconf" ]; then + check_conf_ans=$(check_conf_and_set_vars) + if [ $? -eq 0 ]; then + echo "yes" + else + echo "no ($check_conf_ans)" + fi + exit 0 +fi + +check_conf_and_set_vars 1>&2 || exit 1 + +run_remote() { + if [ -z "$SSH" ]; then + sh -c "$*" + else + $SSH "$*" + fi +} + +run_navicli() { + run_remote "$NAVICLI" "$*" +} + + +# Get Lun List +LUNLIST=$(run_navicli lun -list -drivetype | sed -ne 's/^Name:\ *//p' | sort) + +echo "host_name ${TARGET}" +echo + +if [ "$1" = "config" ] ; then + cat <<-EOF + multigraph emc_vnx_block_blocks + graph_category disk + graph_title EMC VNX 5300 LUN Blocks + graph_vlabel Blocks Read (-) / Written (+) + graph_args --base 1000 + EOF + + while read -r LUN ; do + LUN="$(clean_fieldname "$LUN")" + cat <<-EOF + ${LUN}_read.label none + ${LUN}_read.graph no + ${LUN}_read.min 0 + ${LUN}_read.draw AREA + ${LUN}_read.type COUNTER + ${LUN}_write.label $LUN Blocks + ${LUN}_write.negative ${LUN}_read + ${LUN}_write.type COUNTER + ${LUN}_write.min 0 + ${LUN}_write.draw STACK + EOF + done <<< "$LUNLIST" + + cat <<-EOF + + multigraph emc_vnx_block_req + graph_category disk + graph_title EMC VNX 5300 LUN Requests + graph_vlabel Requests: Read (-) / Write (+) + graph_args --base 1000 + EOF + while read -r LUN ; do + LUN="$(clean_fieldname "$LUN")" + cat <<-EOF + ${LUN}_readreq.label none + ${LUN}_readreq.graph no + ${LUN}_readreq.min 0 + ${LUN}_readreq.type COUNTER + ${LUN}_writereq.label $LUN Requests + ${LUN}_writereq.negative ${LUN}_readreq + ${LUN}_writereq.type COUNTER + ${LUN}_writereq.min 0 + EOF + done <<< "$LUNLIST" + + cat <<-EOF + + multigraph emc_vnx_block_ticks + graph_category disk + graph_title EMC VNX 5300 Counted Load per LUN + graph_vlabel Load, % * Number of LUNs + graph_args --base 1000 -l 0 -r + EOF + echo -n "graph_order " + while read -r LUN ; do + LUN="$(clean_fieldname "$LUN")" + echo -n "${LUN}_busyticks ${LUN}_idleticks ${LUN}_bta=${LUN}_busyticks_spa ${LUN}_idleticks_spa ${LUN}_btb=${LUN}_busyticks_spb ${LUN}_idleticks_spb " + done <<< "$LUNLIST" + echo "" + while read -r LUN ; do + LUN="$(clean_fieldname "$LUN")" + cat <<-EOF + ${LUN}_busyticks_spa.label $LUN Busy Ticks SPA + ${LUN}_busyticks_spa.type COUNTER + ${LUN}_busyticks_spa.graph no + ${LUN}_bta.label $LUN Busy Ticks SPA + ${LUN}_bta.graph no + ${LUN}_idleticks_spa.label $LUN Idle Ticks SPA + ${LUN}_idleticks_spa.type COUNTER + ${LUN}_idleticks_spa.graph no + ${LUN}_busyticks_spb.label $LUN Busy Ticks SPB + ${LUN}_busyticks_spb.type COUNTER + ${LUN}_busyticks_spb.graph no + ${LUN}_btb.label $LUN Busy Ticks SPB + ${LUN}_btb.graph no + ${LUN}_idleticks_spb.label $LUN Idle Ticks SPB + ${LUN}_idleticks_spb.type COUNTER + ${LUN}_idleticks_spb.graph no + ${LUN}_load_spa.label $LUN load SPA + ${LUN}_load_spa.draw AREASTACK + ${LUN}_load_spb.label $LUN load SPB + ${LUN}_load_spb.draw AREASTACK + ${LUN}_load_spa.cdef 100,${LUN}_bta,${LUN}_busyticks_spa,${LUN}_idleticks_spa,+,/,* + ${LUN}_load_spb.cdef 100,${LUN}_btb,${LUN}_busyticks_spa,${LUN}_idleticks_spa,+,/,* + EOF + done <<< "$LUNLIST" + + cat <<-EOF + + multigraph emc_vnx_block_outstanding + graph_category disk + graph_title EMC VNX 5300 Sum of Outstanding Requests + graph_vlabel Requests + graph_args --base 1000 + EOF + while read -r LUN ; do + LUN="$(clean_fieldname "$LUN")" + cat <<-EOF + ${LUN}_outstandsum.label $LUN + ${LUN}_outstandsum.type COUNTER + EOF + done <<< "$LUNLIST" + + cat <<-EOF + + multigraph emc_vnx_block_nonzeroreq + graph_category disk + graph_title EMC VNX 5300 Non-Zero Request Count Arrivals + graph_vlabel Count Arrivals + graph_args --base 1000 + EOF + while read -r LUN ; do + LUN="$(clean_fieldname "$LUN")" + cat <<-EOF + ${LUN}_nonzeroreq.label $LUN + ${LUN}_nonzeroreq.type COUNTER + EOF + done <<< "$LUNLIST" + + cat <<-EOF + + multigraph emc_vnx_block_trespasses + graph_category disk + graph_title EMC VNX 5300 Trespasses + graph_vlabel Trespasses + EOF + while read -r LUN ; do + LUN="$(clean_fieldname "$LUN")" + cat <<-EOF + ${LUN}_implic_tr.label ${LUN} Implicit Trespasses + ${LUN}_explic_tr.label ${LUN} Explicit Trespasses + EOF + done <<< "$LUNLIST" + + cat <<-EOF + + multigraph emc_vnx_block_queue + graph_category disk + graph_title EMC VNX 5300 Counted Block Queue Length + graph_vlabel Length + EOF + while read -r LUN ; do + LUN="$(clean_fieldname "$LUN")" + cat <<-EOF + ${LUN}_busyticks_spa.label ${LUN} + ${LUN}_busyticks_spa.graph no + ${LUN}_busyticks_spa.type COUNTER + ${LUN}_idleticks_spa.label ${LUN} + ${LUN}_idleticks_spa.graph no + ${LUN}_idleticks_spa.type COUNTER + ${LUN}_busyticks_spb.label ${LUN} + ${LUN}_busyticks_spb.graph no + ${LUN}_busyticks_spb.type COUNTER + ${LUN}_idleticks_spb.label ${LUN} + ${LUN}_idleticks_spb.graph no + ${LUN}_idleticks_spb.type COUNTER + ${LUN}_outstandsum.label ${LUN} + ${LUN}_outstandsum.graph no + ${LUN}_outstandsum.type COUNTER + ${LUN}_nonzeroreq.label ${LUN} + ${LUN}_nonzeroreq.graph no + ${LUN}_nonzeroreq.type COUNTER + ${LUN}_readreq.label ${LUN} + ${LUN}_readreq.graph no + ${LUN}_readreq.type COUNTER + ${LUN}_writereq.label ${LUN} + ${LUN}_writereq.graph no + ${LUN}_writereq.type COUNTER + EOF + # Queue Length SPA = ((Sum of Outstanding Requests SPA - NonZero Request Count Arrivals SPA / 2)/(Host Read Requests SPA + Host Write Requests SPA))* + # (Busy Ticks SPA/(Busy Ticks SPA + Idle Ticks SPA) + # We count together SPA and SPB, although it is not fully corrext + cat <<-EOF + ${LUN}_ql_l_a.label ${LUN} Queue Length SPA + ${LUN}_ql_l_a.cdef ${LUN}_outstandsum,${LUN}_nonzeroreq,2,/,-,${LUN}_readreq,${LUN}_writereq,+,/,${LUN}_busyticks_spa,*,${LUN}_busyticks_spa,${LUN}_idleticks_spa,+,/ + ${LUN}_ql_l_b.label ${LUN} Queue Length SPB + ${LUN}_ql_l_b.cdef ${LUN}_outstandsum,${LUN}_nonzeroreq,2,/,-,${LUN}_readreq,${LUN}_writereq,+,/,${LUN}_busyticks_spb,*,${LUN}_busyticks_spb,${LUN}_idleticks_spb,+,/ + EOF + done <<< "$LUNLIST" + cat <<-EOF + + multigraph emc_vnx_block_ticks_total + graph_category disk + graph_title EMC VNX 5300 Counted Load per SP + graph_vlabel Load, % + EOF + echo -n "graph_order " + for SP in $SPALL; do + SPclean="$(clean_fieldname "$SP")" + echo -n "${SPclean}_total_bt=${SPclean}_total_busyticks " + done + echo "" + for SP in $SPALL; do + SPclean="$(clean_fieldname "$SP")" + cat <<-EOF + ${SPclean}_total_busyticks.label ${SP} + ${SPclean}_total_busyticks.graph no + ${SPclean}_total_busyticks.type COUNTER + ${SPclean}_total_bt.label ${SP} + ${SPclean}_total_bt.graph no + ${SPclean}_total_bt.type COUNTER + ${SPclean}_total_idleticks.label ${SP} + ${SPclean}_total_idleticks.graph no + ${SPclean}_total_idleticks.type COUNTER + ${SPclean}_total_load.label ${SP} Total Load + ${SPclean}_total_load.cdef ${SPclean}_total_bt,${SPclean}_total_busyticks,${SPclean}_total_idleticks,+,/,100,* + EOF + done + exit 0 +fi + +#Preparing big complex command to SP's to have most work done remotely. +#BIGCMD="$SSH" +while read -r LUN ; do + FILTERLUN="$(clean_fieldname "$LUN")" + BIGCMD+="$NAVICLI lun -list -name $LUN -perfData | + sed -ne 's/^Blocks Read\:\ */${FILTERLUN}_read.value /p; + s/^Blocks Written\:\ */${FILTERLUN}_write.value /p; + s/Read Requests\:\ */${FILTERLUN}_readreq.value /p; + s/Write Requests\:\ */${FILTERLUN}_writereq.value /p; + s/Busy Ticks SP A\:\ */${FILTERLUN}_busyticks_spa.value /p; + s/Idle Ticks SP A\:\ */${FILTERLUN}_idleticks_spa.value /p; + s/Busy Ticks SP B\:\ */${FILTERLUN}_busyticks_spb.value /p; + s/Idle Ticks SP B\:\ */${FILTERLUN}_idleticks_spb.value /p; + s/Sum of Outstanding Requests\:\ */${FILTERLUN}_outstandsum.value /p; + s/Non-Zero Request Count Arrivals\:\ */${FILTERLUN}_nonzeroreq.value /p; + s/Implicit Trespasses\:\ */${FILTERLUN}_implic_tr.value /p; + s/Explicit Trespasses\:\ */${FILTERLUN}_explic_tr.value /p; + ' ; " +done <<< "$LUNLIST" +ANSWER=$(run_remote "$BIGCMD") + +for SP in $SPALL; do + FILTER_SP="$(clean_fieldname "$SP")" + BIGCMD="getcontrol -cbt | sed -ne ' + s/Controller busy ticks\:\ */${FILTER_SP}_total_busyticks.value /p; + s/Controller idle ticks\:\ */${FILTER_SP}_total_idleticks.value /p; + ' + " + ANSWER+=$'\n'$(run_remote "$NAVICLI_NOSP $SP" "$BIGCMD") +done + +get_precise_answer_field() { + echo "$ANSWER" | grep -F "_${1}." +} + +echo "multigraph emc_vnx_block_blocks" +get_precise_answer_field "read" +get_precise_answer_field "write" +echo -e "\nmultigraph emc_vnx_block_req" +get_precise_answer_field "readreq" +get_precise_answer_field "writereq" + +echo -e "\nmultigraph emc_vnx_block_ticks" +while read -r LUN ; do + LUN="$(clean_fieldname "$LUN")" + #Will count these values later, using cdef + echo "${LUN}_load_spa.value 0" + echo "${LUN}_load_spb.value 0" +done <<< "$LUNLIST" +get_precise_answer_field "busyticks_spa" +get_precise_answer_field "idleticks_spa" +get_precise_answer_field "busyticks_spb" +get_precise_answer_field "idleticks_spb" + +echo -e "\nmultigraph emc_vnx_block_outstanding" +get_precise_answer_field "outstandsum" + +echo -e "\nmultigraph emc_vnx_block_nonzeroreq" +get_precise_answer_field "nonzeroreq" + +echo -e "\nmultigraph emc_vnx_block_trespasses" +get_precise_answer_field "implic_tr" +get_precise_answer_field "explic_tr" + +echo -e "\nmultigraph emc_vnx_block_queue" +# Queue Length +get_precise_answer_field "busyticks_spa" +get_precise_answer_field "idleticks_spa" +get_precise_answer_field "busyticks_spb" +get_precise_answer_field "idleticks_spb" +get_precise_answer_field "outstandsum" +get_precise_answer_field "nonzeroreq" +get_precise_answer_field "readreq" +get_precise_answer_field "writereq" +while read -r LUN ; do + LUN="$(clean_fieldname "$LUN")" + #Will count these values later, using cdef + echo "${LUN}_ql_l_a.value 0 " + echo "${LUN}_ql_l_b.value 0 " +done <<< "$LUNLIST" + +echo -e "\nmultigraph emc_vnx_block_ticks_total" +get_precise_answer_field "total_busyticks" +get_precise_answer_field "total_idleticks" +#Will count them later +for SP in $SPALL; do + SP="$(clean_fieldname "$SP")" + echo "${SP}_total_load.value 0" +done + +exit 0 diff --git a/plugins/emc/emc_vnx_file_ b/plugins/emc/emc_vnx_file_ new file mode 100755 index 00000000..45bda578 --- /dev/null +++ b/plugins/emc/emc_vnx_file_ @@ -0,0 +1,723 @@ +#!/bin/bash + +: <<=cut + +=head1 NAME + + emc_vnx_file_stats - Plugin to monitor Basic, NFSv3 and NFSv4 statistics of + EMC VNX 5300 Unified Storage system's Datamovers + +=head1 AUTHOR + + Evgeny Beysembaev + +=head1 LICENSE + + GPLv2 + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf suggest + +=head1 DESCRIPTION + + The plugin monitors basic statistics of EMC Unified Storage system Datamovers + and NFS statistics of EMC VNX5300 Unified Storage system. Probably it can + also be compatible with other Isilon or Celerra systems. It uses SSH to connect + to Control Stations, then remotely executes '/nas/sbin/server_stats' and + fetches and parses data from it. It supports gathering data both from + active/active and active/passive Datamover configurations, ignoring offline or + standby Datamovers. + If all Datamovers are offline or absent, the plugin returns error. + This plugin also automatically chooses Primary Control Station from the list by + calling '/nasmcd/sbin/getreason' and '/nasmcd/sbin/t2slot'. + + At the moment data is gathered from the following statistics sources: + * nfs.v3.op - Tons of timings about NFSv3 RPC calls + * nfs.v4.op - Tons of timings about NFSv4 RPC calls + * nfs.client - Here new Client addresses are rescanned and added automatically. + * basic-std Statistics Group - Basic Statistics of Datamovers (eg. CPU, Memory + etc.) + + It's quite easy to comment out unneeded data to make graphs less overloaded or + to add new statistics sources. + + The plugin has been tested in the following Operating Environment (OE): + File Version T7.1.76.4 + Block Revision 05.32.000.5.215 + +=head1 LIST OF GRAPHS + + These are Basic Datamover Graphs. + Graph category CPU: + EMC VNX 5300 Datamover CPU Util % + Graph category Network: + EMC VNX 5300 Datamover Network bytes over all interfaces + EMC VNX 5300 Datamover Storage bytes over all interfaces + Graph category Memory: + EMC VNX 5300 Datamover Memory + EMC VNX 5300 File Buffer Cache + EMC VNX 5300 FileResolve + + These are NFS (v3,v4) Graphs. + Graph category FS: + EMC VNX 5300 NFSv3 Calls per second + EMC VNX 5300 NFSv3 uSeconds per call + EMC VNX 5300 NFSv3 Op % + EMC VNX 5300 NFSv4 Calls per second + EMC VNX 5300 NFSv4 uSeconds per call + EMC VNX 5300 NFSv4 Op % + EMC VNX 5300 NFS Client Ops/s + EMC VNX 5300 NFS Client B/s + EMC VNX 5300 NFS Client Avg uSec/call + EMC VNX 5300 Std NFS Ops/s + EMC VNX 5300 Std NFS B/s + EMC VNX 5300 Std NFS Average Size Bytes + EMC VNX 5300 Std NFS Active Threads + +=head1 COMPATIBILITY + + The plugin has been written for being compatible with EMC VNX5300 Storage + system, as this is the only EMC storage which i have. + By the way, i am pretty sure it can also work with other VNX1 storages, like + VNX5100 and VNX5500. + About VNX2 series, i don't know whether the plugin will be able to work with + them. Maybe it would need some corrections in command-line backend. The same + situation is with other EMC systems, so i encourage you to try and fix the + plugin. + +=head1 CONFIGURATION + + The plugin uses SSH to connect to Control Stations. It's possible to use + 'nasadmin' user, but it would be better if you create read-only global user by + Unisphere Client. The user should have only Operator role. + I created "operator" user but due to the fact that Control Stations already + had one internal "operator" user, the new one was called "operator1". So be + careful. After that, copy .bash_profile from /home/nasadmin to a newly created + /home/operator1 + + On munin-node side choose a user which will be used to connect through SSH. + Generally user "munin" is ok. Then, execute "sudo su munin -s /bin/bash", + "ssh-keygen" and "ssh-copy-id" to both Control Stations with newly created + user. + + Make a link from /usr/share/munin/plugins/emc_vnx_file_stats to + /etc/munin/plugins/. If you want to get NFS statistics, name the link as + "emc_vnx_file_nfs_stats_", otherwise to get Basic Datamover statistics + you have to name it "emc_vnx_file_basicdm_stats_", where is any + arbitrary name of your storage system. The plugin will return in its + answer as "host_name" field. + + For example, assume your storage system is called "VNX5300". + Make a configuration file at + /etc/munin/plugin-conf.d/emc_vnx_file_stats_VNX5300 + + [emc_vnx_file_*] + user munin + env.username operator1 + env.cs_addr 192.168.1.1 192.168.1.2 + env.nas_servers server_2 server_3 + + Where: + user - SSH Client local user + env.username - Remote user with Operator role + env.cs_addr - Control Stations addresses + env.nas_servers - This is the default value and can be omitted + +=head1 HISTORY + + 08.11.2016 - First Release + 17.11.2016 - NFSv4 support, Memory section + 16.12.2016 - Merged "NFS" and "Datamover Stats" plugins + 26.12.2016 - Compatibility with Munin coding style + +=cut + +export LANG=C + +. "$MUNIN_LIBDIR/plugins/plugin.sh" + +nas_server_ok="" +cs_addr=${cs_addr:=""} +username=${username:=""} +nas_servers=${nas_servers:="server_2 server_3"} + +# Prints "10" on stdout if found Primary Online control station. "11" - for Secondary Online control station. +ssh_check_cmd() { + ssh -q "$username@$1" "/nasmcd/sbin/getreason | grep -w \"slot_\$(/nasmcd/sbin/t2slot)\" | cut -d- -f1 | awk '{print \$1}' " + +} + +check_conf () { + if [ -z "$username" ]; then + echo "No username ('username' environment variable)!" + return 1 + fi + + if [ -z "$cs_addr" ]; then + echo "No control station addresses ('cs_addr' environment variable)!" + return 1 + fi + + #Choosing Cotrol Station. Code have to be "10" + for CS in $cs_addr; do + if [[ "10" = "$(ssh_check_cmd "$CS")" ]]; then + PRIMARY_CS=$CS + break + fi + done + + if [ -z "$PRIMARY_CS" ]; then + echo "No alive primary Control Station from list \"$cs_addr\""; + return 1 + fi + return 0 +} + +if [ "$1" = "autoconf" ]; then + check_conf_ans=$(check_conf) + if [ $? -eq 0 ]; then + echo "yes" + else + echo "no ($check_conf_ans)" + fi + exit 0 +fi + +if [ "$1" = "suggest" ]; then + echo "nfs_stats" + echo "basicdm_stats" + exit 0; +fi + +STATSTYPE=$(echo "${0##*/}" | cut -d _ -f 1-5) +if [ "$STATSTYPE" = "emc_vnx_file_nfs_stats" ]; then STATSTYPE=NFS; +elif [ "$STATSTYPE" = "emc_vnx_file_basicdm_stats" ]; then STATSTYPE=BASICDM; +else echo "Do not know what to do. Name the plugin as 'emc_vnx_file_nfs_stats_' or 'emc_vnx_file_basicdm_stats_'" >&2; exit 1; fi + +TARGET=$(echo "${0##*/}" | cut -d _ -f 6) + +check_conf 1>&2 || exit 1 + +run_remote () { + # shellcheck disable=SC2029 + ssh -q "$username@$PRIMARY_CS" ". /home/$username/.bash_profile; $*" +} + +echo "host_name ${TARGET}" + +if [ "$1" = "config" ] ; then +# TODO: active/active + for server in $nas_servers; do + run_remote nas_server -i "$server" | grep -q 'type *= nas' || continue + nas_server_ok=TRUE + filtered_server="$(clean_fieldname "$server")" + + if [ "$STATSTYPE" = "BASICDM" ] ; then + cat <<-EOF + multigraph emc_vnx_cpu_percent + graph_title EMC VNX 5300 Datamover CPU Util % + graph_vlabel % + graph_category cpu + graph_scale no + graph_args --upper-limit 100 -l 0 + ${server}_cpuutil.min 0 + ${server}_cpuutil.label $server CPU util. in %. + + multigraph emc_vnx_network_b + graph_title EMC VNX 5300 Datamover Network bytes over all interfaces + graph_vlabel B/s recv. (-) / sent (+) + graph_category network + graph_args --base 1000 + ${server}_net_in.graph no + ${server}_net_in.label none + ${server}_net_out.label $server B/s + ${server}_net_out.negative ${server}_net_in + ${server}_net_out.draw AREA + + multigraph emc_vnx_storage_b + graph_title EMC VNX 5300 Datamover Storage bytes over all interfaces + graph_vlabel B/s recv. (-) / sent (+) + graph_category network + graph_args --base 1000 + ${server}_stor_read.graph no + ${server}_stor_read.label none + ${server}_stor_write.label $server B/s + ${server}_stor_write.negative ${server}_stor_read + ${server}_stor_write.draw AREA + + multigraph emc_vnx_memory + graph_title EMC VNX 5300 Datamover Memory + graph_vlabel KiB + graph_category memory + graph_args --base 1024 + graph_order ${server}_used ${server}_free ${server}_total ${server}_freebuffer ${server}_encumbered + ${server}_used.label ${server} Used + ${server}_free.label ${server} Free + ${server}_free.draw STACK + ${server}_total.label ${server} Total + ${server}_freebuffer.label ${server} Free Buffer + ${server}_encumbered.label ${server} Encumbered + + multigraph emc_vnx_filecache + graph_title EMC VNX 5300 File Buffer Cache + graph_vlabel per second + graph_category memory + graph_args --base 1000 + graph_order ${server}_highw_hits ${server}_loww_hits ${server}_w_hits ${server}_hits ${server}_lookups + ${server}_highw_hits.label High Watermark Hits + ${server}_loww_hits.label Low Watermark Hits + ${server}_loww_hits.draw STACK + ${server}_w_hits.label Watermark Hits + ${server}_hits.label Hits + ${server}_lookups.label Lookups + + multigraph emc_vnx_fileresolve + graph_title EMC VNX 5300 FileResolve + graph_vlabel Entries + graph_category memory + graph_args --base 1000 + ${server}_dropped.label Dropped Entries + ${server}_max.label Max Limit + ${server}_used.label Used Entries + EOF + fi + if [ "$STATSTYPE" = "NFS" ] ; then +#nfs.v3.op data +# [nasadmin@mnemonic0 ~]$ server_stats server_2 -info nfs.v3.op +# server_2 : +# +# name = nfs.v3.op +# description = NFS V3 per operation statistics +# type = Set +# member_stats = nfs.v3.op.ALL-ELEMENTS.calls,nfs.v3.op.ALL-ELEMENTS.failures,nfs.v3.op.ALL-ELEMENTS.avgTime,nfs.v3.op.ALL-ELEMENTS.opPct +# member_elements = nfs.v3.op.v3Null,nfs.v3.op.v3GetAttr,nfs.v3.op.v3SetAttr,nfs.v3.op.v3Lookup,nfs.v3.op.v3Access,nfs.v3.op.v3ReadLink,nfs.v3.op.v3Read,nfs.v3.op.v3Write,nfs.v3.op.v3Create,nfs.v3.op.v3Mkdir,nfs.v3.op.v3Symlink,nfs.v3.op.v3Mknod,nfs.v3.op.v3Remove,nfs.v3.op.v3Rmdir,nfs.v3.op.v3Rename,nfs.v3.op.v3Link,nfs.v3.op.v3ReadDir,nfs.v3.op.v3ReadDirPlus,nfs.v3.op.v3FsStat,nfs.v3.op.v3FsInfo,nfs.v3.op.v3PathConf,nfs.v3.op.v3Commit,nfs.v3.op.VAAI +# member_of = nfs.v3 + member_elements_by_line=$(run_remote server_stats "$server" -info nfs.v3.op | grep member_elements | sed -ne 's/^.*= //p') + IFS=',' read -ra graphs <<< "$member_elements_by_line" + cat <<-EOF + multigraph vnx_emc_v3_calls_s + graph_title EMC VNX 5300 NFSv3 Calls per second + graph_vlabel Calls + graph_category fs + graph_args --base 1000 + EOF + for graph in "${graphs[@]}"; do + field=$(echo "$graph" | cut -d '.' -f4 ) + echo "${server}_$field.label $server $field" + done + + cat <<-EOF + + multigraph vnx_emc_v3_usec_call + graph_title EMC VNX 5300 NFSv3 uSeconds per call + graph_vlabel uSec / call + graph_category fs + graph_args --base 1000 + EOF + for graph in "${graphs[@]}"; do + field=$(echo "$graph" | cut -d '.' -f4 ) + echo "${server}_$field.label $server $field" + done + cat <<-EOF + + multigraph vnx_emc_v3_op_percent + graph_title EMC VNX 5300 NFSv3 Op % + graph_vlabel % + graph_scale no + graph_category fs + EOF + for graph in "${graphs[@]}"; do + field=$(echo "$graph" | cut -d '.' -f4 ) + echo "${server}_$field.label $server $field" + echo "${server}_$field.min 0" + done + graphs=() +#nfs.v4.op data + member_elements_by_line=$(run_remote server_stats "$server" -info nfs.v4.op | grep member_elements | sed -ne 's/^.*= //p') + IFS=',' read -ra graphs <<< "$member_elements_by_line" + cat <<-EOF + multigraph vnx_emc_v4_calls_s + graph_title EMC VNX 5300 NFSv4 Calls per second + graph_vlabel Calls + graph_category fs + graph_args --base 1000 + EOF + for graph in "${graphs[@]}"; do + field=$(echo "$graph" | cut -d '.' -f4 ) + echo "${server}_$field.label $server $field" + done + + cat <<-EOF + + multigraph vnx_emc_v4_usec_call + graph_title EMC VNX 5300 NFSv4 uSeconds per call + graph_vlabel uSec / call + graph_category fs + graph_args --base 1000 + EOF + for graph in "${graphs[@]}"; do + field=$(echo "$graph" | cut -d '.' -f4 ) + echo "${server}_$field.label $server $field" + done + cat <<-EOF + + multigraph vnx_emc_v4_op_percent + graph_title EMC VNX 5300 NFSv4 Op % + graph_vlabel % + graph_scale no + graph_category fs + EOF + for graph in "${graphs[@]}"; do + field=$(echo "$graph" | cut -d '.' -f4 ) + echo "${server}_$field.label $server $field" + echo "${server}_$field.min 0" + done + +#nfs.client data +# Total Read Write Suspicious Total Read Write Avg +# Ops/s Ops/s Ops/s Ops diff KiB/s KiB/s KiB/s uSec/call + member_elements_by_line=$(run_remote server_stats server_2 -monitor nfs.client -count 1 -terminationsummary no -titles never | sed -ne 's/^.*id=//p' | cut -d' ' -f1) + #Somewhy readarray adds extra \n in the end of each variable. So, we use read() with a workaround + IFS=$'\n' read -rd '' -a graphs_array <<< "$member_elements_by_line" + cat <<-EOF + + multigraph vnx_emc_nfs_client_ops_s + graph_title EMC VNX 5300 NFS Client Ops/s + graph_vlabel Ops/s + graph_category fs + EOF + echo -n "graph_order " + for graph in "${graphs_array[@]}"; do + field="$(clean_fieldname "_$graph")" + echo -n "${server}${field}_r ${server}${field}_w ${server}${field}_t ${server}${field}_s " + done + echo " " + for graph in "${graphs_array[@]}"; do + field="$(clean_fieldname "_$graph")" + echo "${server}${field}_r.label $server $graph Read Ops/s" + echo "${server}${field}_w.label $server $graph Write Ops/s" + echo "${server}${field}_w.draw STACK" + echo "${server}${field}_t.label $server $graph Total Ops/s" + echo "${server}${field}_s.label $server $graph Suspicious Ops diff" + done + + cat <<-EOF + + multigraph vnx_emc_nfs_client_b_s + graph_title EMC VNX 5300 NFS Client B/s + graph_vlabel B/s + graph_category fs + EOF + echo -n "graph_order " + for graph in "${graphs_array[@]}"; do + field="$(clean_fieldname "_$graph")" + echo -n "${server}${field}_r ${server}${field}_w ${server}${field}_t " + done + echo " " + for graph in "${graphs_array[@]}"; do + field="$(clean_fieldname "_$graph")" + echo "${server}${field}_r.label $server $graph Read B/s" + echo "${server}${field}_w.label $server $graph Write B/s" + echo "${server}${field}_w.draw STACK" + echo "${server}${field}_t.label $server $graph Total B/s" + done + + cat <<-EOF + + multigraph vnx_emc_nfs_client_avg_usec + graph_title EMC VNX 5300 NFS Client Avg uSec/call + graph_vlabel uSec/call + graph_category fs + EOF + for graph in "${graphs_array[@]}"; do + field="$(clean_fieldname "_$graph")" + echo "${server}${field}.label $server $graph Avg uSec/call" + done + +#nfs-std +# Timestamp NFS Read Read Read Size Write Write Write Size Active +# Ops/s Ops/s KiB/s Bytes Ops/s KiB/s Bytes Threads + cat <<-EOF + + multigraph vnx_emc_nfs_std_nfs_ops + graph_title EMC VNX 5300 Std NFS Ops/s + graph_vlabel Ops/s + graph_category fs + EOF + echo "graph_order ${filtered_server}_rops ${filtered_server}_wops ${filtered_server}_tops" + echo "${filtered_server}_rops.label $server Read Ops/s" + echo "${filtered_server}_wops.label $server Write Ops/s" + echo "${filtered_server}_wops.draw STACK" + echo "${filtered_server}_tops.label $server Total Ops/s" + + cat <<-EOF + + multigraph vnx_emc_nfs_std_nfs_b_s + graph_title EMC VNX 5300 Std NFS B/s + graph_vlabel B/s + graph_category fs + EOF + echo "graph_order ${filtered_server}_rbs ${filtered_server}_wbs ${filtered_server}_tbs" + echo "${filtered_server}_rbs.label $server Read B/s" + echo "${filtered_server}_wbs.label $server Write B/s" + echo "${filtered_server}_wbs.draw STACK" + echo "${filtered_server}_tbs.label $server Total B/s" + echo "${filtered_server}_tbs.cdef ${filtered_server}_rbs,${filtered_server}_wbs,+" + + cat <<-EOF + + multigraph vnx_emc_nfs_std_nfs_avg + graph_title EMC VNX 5300 Std NFS Average Size Bytes + graph_vlabel Bytes + graph_category fs + EOF + echo "${filtered_server}_avg_readsize.label $server Average Read Size Bytes" + echo "${filtered_server}_avg_writesize.label $server Average Write Size Bytes" + + cat <<-EOF + + multigraph vnx_emc_nfs_std_nfs_threads + graph_title EMC VNX 5300 Std NFS Active Threads + graph_vlabel Threads + graph_category fs + EOF + echo "${filtered_server}_threads.label $server Active Threads" + fi + done + if [ -z "$nas_server_ok" ]; then + echo "No active data movers!" 1>&2 + fi + exit 0 +fi + +for server in $nas_servers; do + run_remote nas_server -i "$server" | grep -q 'type *= nas' || continue + nas_server_ok=TRUE + filtered_server="$(clean_fieldname "$server")" + + if [ "$STATSTYPE" = "BASICDM" ] ; then +#basicdm data +# [nasadmin@mnemonic0 ~]$ server_stats server_2 -count 1 -terminationsummary no +# server_2 CPU Network Network dVol dVol +# Timestamp Util In Out Read Write +# % KiB/s KiB/s KiB/s KiB/s +# 20:42:26 9 16432 3404 1967 24889 + + member_elements_by_line=$(run_remote server_stats "$server" -count 1 -terminationsummary no -titles never | grep '^[^[:space:]]') + IFS=$' ' read -ra graphs <<< "$member_elements_by_line" + + echo "multigraph emc_vnx_cpu_percent" + echo "${server}_cpuutil.value ${graphs[1]}" + + echo -e "\nmultigraph emc_vnx_network_b" + echo "${server}_net_in.value $((graphs[2] * 1024))" + echo "${server}_net_out.value $((graphs[3] * 1024))" + + echo -e "\nmultigraph emc_vnx_storage_b" + echo "${server}_stor_read.value $((graphs[4] * 1024))" + echo "${server}_stor_write.value $((graphs[5] * 1024))" + +# [nasadmin@mnemonic0 ~]$ server_stats server_2 -monitor kernel.memory -count 1 -terminationsummary no +# server_2 Free Buffer Buffer Buffer Buffer Buffer Buffer Cache Encumbered FileResolve FileResolve FileResolve Free KiB Page Total Used KiB Memory +# Timestamp Buffer Cache High Cache Cache Cache Cache Low Watermark Memory Dropped Max Used Size Memory Util +# KiB Watermark Hits/s Hit % Hits/s Lookups/s Watermark Hits/s Hits/s KiB Entries Limit Entries KiB KiB % +# 20:44:14 3522944 0 96 11562 12010 0 0 3579268 0 0 0 3525848 8 6291456 2765608 44 + + member_elements_by_line=$(run_remote server_stats "$server" -monitor kernel.memory -count 1 -terminationsummary no -titles never | grep '^[^[:space:]]') + IFS=$' ' read -ra graphs <<< "$member_elements_by_line" + + echo -e "\nmultigraph emc_vnx_memory" + #Reserved for math + echo "${server}_total.value $((graphs[14] / 1))" + echo "${server}_used.value $((graphs[15] / 1))" + echo "${server}_free.value $((graphs[12] / 1))" + echo "${server}_freebuffer.value $((graphs[1] / 1))" + echo "${server}_encumbered.value $((graphs[8] / 1))" + + echo -e "\nmultigraph emc_vnx_filecache" + echo "${server}_highw_hits.value ${graphs[2]}" + echo "${server}_loww_hits.value ${graphs[6]}" + echo "${server}_w_hits.value ${graphs[7]}" + echo "${server}_hits.value ${graphs[4]}" + echo "${server}_lookups.value ${graphs[5]}" + + echo -e "\nmultigraph emc_vnx_fileresolve" + echo "${server}_dropped.value ${graphs[9]}" + echo "${server}_max.value ${graphs[10]}" + echo "${server}_used.value ${graphs[11]}" + + + fi + if [ "$STATSTYPE" = "NFS" ] ; then +#nfs.v3.op data +# [nasadmin@mnemonic0 ~]$ server_stats server_2 -monitor nfs.v3.op -count 1 -terminationsummary no +# server_2 NFS Op NFS NFS Op NFS NFS Op % +# Timestamp Op Errors Op +# Calls/s diff uSec/Call +# 22:14:41 v3GetAttr 30 0 23 21 +# v3Lookup 40 0 98070 27 +# v3Access 50 0 20 34 +# v3Read 4 0 11180 3 +# v3Write 2 0 2334 1 +# v3Create 1 0 1743 1 +# v3Mkdir 13 0 953 9 +# v3Link 6 0 1064 4 + + member_elements_by_line=$(run_remote server_stats "$server" -monitor nfs.v3.op -count 1 -terminationsummary no -titles never | sed -ne 's/^.*v3/v3/p') + NUMCOL=5 + LINES=$(wc -l <<< "$member_elements_by_line") + while IFS=$'\n' read -ra graphs ; do + elements_array+=( $graphs ) + done <<< "$member_elements_by_line" + + if [ "${#elements_array[@]}" -eq "0" ]; then LINES=0; fi + + echo "multigraph vnx_emc_v3_calls_s" + for ((i=0; i<$((LINES)); i++ )); do + echo "${server}_${elements_array[i*$NUMCOL]}".value "${elements_array[i*$NUMCOL+1]}" + done + + echo -e "\nmultigraph vnx_emc_v3_usec_call" + for ((i=0; i<$((LINES)); i++ )); do + echo "${server}_${elements_array[i*$NUMCOL]}".value "${elements_array[i*$NUMCOL+3]}" + done + + echo -e "\nmultigraph vnx_emc_v3_op_percent" + for ((i=0; i<$((LINES)); i++ )); do + echo "${server}_${elements_array[i*$NUMCOL]}".value "${elements_array[i*$NUMCOL+4]}" + done + + elements_array=() + +#nfs.v4.op data +# [nasadmin@mnemonic0 ~]$ server_stats server_2 -monitor nfs.v4.op -count 1 -terminationsummary no +# server_2 NFS Op NFS NFS Op NFS NFS Op % +# Timestamp Op Errors Op +# Calls/s diff uSec/Call +# 22:13:14 v4Compound 2315 0 7913 30 +# v4Access 246 0 5 3 +# v4Close 133 0 11 2 +# v4Commit 2 0 6928 0 +# v4Create 1 0 881 0 +# v4DelegRet 84 0 19 1 +# v4GetAttr 1330 0 7 17 +# v4GetFh 164 0 3 2 +# v4Lookup 68 0 43 1 +# v4Open 132 0 1061 2 +# v4PutFh 2314 0 11 30 +# v4Read 359 0 15561 5 +# v4ReadDir 1 0 37 0 +# v4Remove 62 0 1096 1 +# v4Rename 1 0 947 0 +# v4Renew 2 0 3 0 +# v4SaveFh 1 0 3 0 +# v4SetAttr 9 0 889 0 +# v4Write 525 0 16508 7 + + member_elements_by_line=$(run_remote server_stats "$server" -monitor nfs.v4.op -count 1 -terminationsummary no -titles never | sed -ne 's/^.*v4/v4/p') + NUMCOL=5 + LINES=$(wc -l <<< "$member_elements_by_line") + while IFS=$'\n' read -ra graphs ; do + elements_array+=( $graphs ) + done <<< "$member_elements_by_line" + + if [ "${#elements_array[@]}" -eq "0" ]; then LINES=0; fi + + echo -e "\nmultigraph vnx_emc_v4_calls_s" + for ((i=0; i<$((LINES)); i++ )); do + echo "${server}_${elements_array[i*$NUMCOL]}".value "${elements_array[i*$NUMCOL+1]}" + done + + echo -e "\nmultigraph vnx_emc_v4_usec_call" + for ((i=0; i<$((LINES)); i++ )); do + echo "${server}_${elements_array[i*$NUMCOL]}".value "${elements_array[i*$NUMCOL+3]}" + done + + echo -e "\nmultigraph vnx_emc_v4_op_percent" + for ((i=0; i<$((LINES)); i++ )); do + echo "${server}_${elements_array[i*$NUMCOL]}".value "${elements_array[i*$NUMCOL+4]}" + done + + elements_array=() + +#nfs.client data +# [nasadmin@mnemonic0 ~]$ server_stats server_2 -monitor nfs.client -count 1 -terminationsummary no +# server_2 Client NFS NFS NFS NFS NFS NFS NFS NFS +# Timestamp Total Read Write Suspicious Total Read Write Avg +# Ops/s Ops/s Ops/s Ops diff KiB/s KiB/s KiB/s uSec/call +# 20:26:38 id=192.168.1.223 2550 20 2196 13 4673 159 4514 1964 +# id=192.168.1.2 691 4 5 1 1113 425 688 2404 +# id=192.168.1.1 159 0 0 51 0 0 0 6017 +# id=192.168.1.6 37 4 2 0 586 295 291 5980 +# id=192.168.1.235 21 1 0 0 0 0 0 155839 +# id=192.168.1.224 5 0 5 0 20 0 20 704620 + + member_elements_by_line=$(run_remote server_stats server_2 -monitor nfs.client -count 1 -terminationsummary no -titles never | sed -ne 's/^.*id=//p') + echo -e "\nmultigraph vnx_emc_nfs_client_ops_s" + NUMCOL=9 + LINES=$(wc -l <<< "$member_elements_by_line") + while IFS=$'\n' read -ra graphs; do + elements_array+=($graphs) + done <<< "$member_elements_by_line" + + #Not drawing elements in case of empty set + if [ "${#elements_array[@]}" -eq "0" ]; then LINES=0; fi + + for (( i=0; i<$((LINES)); i++ )); do + client="$(clean_fieldname "_${elements_array[i*$NUMCOL]}")" + echo "${server}${client}_r".value "${elements_array[$i*$NUMCOL+2]}" + echo "${server}${client}_w".value "${elements_array[$i*$NUMCOL+3]}" + echo "${server}${client}_t".value "${elements_array[$i*$NUMCOL+1]}" + echo "${server}${client}_s".value "${elements_array[$i*$NUMCOL+4]}" + done + echo -e "\nmultigraph vnx_emc_nfs_client_b_s" + for (( i=0; i<$((LINES)); i++ )); do + client="$(clean_fieldname "_${elements_array[i*$NUMCOL]}")" + echo "${server}${client}_r".value "$((${elements_array[$i*$NUMCOL+6]} * 1024))" + echo "${server}${client}_w".value "$((${elements_array[$i*$NUMCOL+7]} * 1024))" + echo "${server}${client}_t".value "$((${elements_array[$i*$NUMCOL+5]} * 1024))" + done + echo -e "\nmultigraph vnx_emc_nfs_client_avg_usec" + for (( i=0; i<$((LINES)); i++ )); do + client="$(clean_fieldname "_${elements_array[i*$NUMCOL]}")" + echo "${server}${client}".value "${elements_array[$i*$NUMCOL+8]}" + done + +#nfs-std +# bash-3.2$ server_stats server_2 -monitor nfs-std +# server_2 Total NFS NFS NFS Avg NFS NFS NFS Avg NFS +# Timestamp NFS Read Read Read Size Write Write Write Size Active +# Ops/s Ops/s KiB/s Bytes Ops/s KiB/s Bytes Threads +# 18:14:52 688 105 6396 62652 1 137 174763 3 + member_elements_by_line=$(run_remote server_stats "$server" -monitor nfs-std -count 1 -terminationsummary no -titles never | grep '^[^[:space:]]') + IFS=$' ' read -ra graphs <<< "$member_elements_by_line" +# echo "$member_elements_by_line" +# echo "${graphs[@]}" + + echo -e "\nmultigraph vnx_emc_nfs_std_nfs_ops" + echo "${filtered_server}_rops.value ${graphs[2]}" + echo "${filtered_server}_wops.value ${graphs[5]}" + echo "${filtered_server}_tops.value ${graphs[1]}" + + echo -e "\nmultigraph vnx_emc_nfs_std_nfs_b_s" + echo "${filtered_server}_rbs.value $((graphs[3] * 1024))" + echo "${filtered_server}_wbs.value $((graphs[6] * 1024))" + echo "${filtered_server}_tbs.value 0" + + + echo -e "\nmultigraph vnx_emc_nfs_std_nfs_avg" + echo "${filtered_server}_avg_readsize.value ${graphs[4]}" + echo "${filtered_server}_avg_writesize.value ${graphs[7]}" + + + echo -e "\nmultigraph vnx_emc_nfs_std_nfs_threads" + echo "${filtered_server}_threads.value ${graphs[8]}" + fi +done +if [ -z "$nas_server_ok" ]; then + echo "No active data movers!" 1>&2 +fi +exit 0 + diff --git a/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-1.png b/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-1.png new file mode 100644 index 00000000..259a9d0b Binary files /dev/null and b/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-1.png differ diff --git a/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-2.png b/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-2.png new file mode 100644 index 00000000..f4ad6882 Binary files /dev/null and b/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-2.png differ diff --git a/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-3.png b/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-3.png new file mode 100644 index 00000000..868d80ff Binary files /dev/null and b/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-3.png differ diff --git a/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-4.png b/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-4.png new file mode 100644 index 00000000..5ff1847e Binary files /dev/null and b/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-4.png differ diff --git a/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-5.png b/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-5.png new file mode 100644 index 00000000..f234b40f Binary files /dev/null and b/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-5.png differ diff --git a/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-6.png b/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-6.png new file mode 100644 index 00000000..fd86a192 Binary files /dev/null and b/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-6.png differ diff --git a/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-7.png b/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-7.png new file mode 100644 index 00000000..0925f136 Binary files /dev/null and b/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-7.png differ diff --git a/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-8.png b/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-8.png new file mode 100644 index 00000000..b031f3d3 Binary files /dev/null and b/plugins/emc/example-graphs/emc_vnx_block_lun_perfdata-8.png differ diff --git a/plugins/emc/example-graphs/emc_vnx_file_-1.png b/plugins/emc/example-graphs/emc_vnx_file_-1.png new file mode 100644 index 00000000..03e475b8 Binary files /dev/null and b/plugins/emc/example-graphs/emc_vnx_file_-1.png differ diff --git a/plugins/emc/example-graphs/emc_vnx_file_-2.png b/plugins/emc/example-graphs/emc_vnx_file_-2.png new file mode 100644 index 00000000..0d72895f Binary files /dev/null and b/plugins/emc/example-graphs/emc_vnx_file_-2.png differ diff --git a/plugins/emc/example-graphs/emc_vnx_file_-3.png b/plugins/emc/example-graphs/emc_vnx_file_-3.png new file mode 100644 index 00000000..9f65d9e5 Binary files /dev/null and b/plugins/emc/example-graphs/emc_vnx_file_-3.png differ diff --git a/plugins/emc/example-graphs/emc_vnx_file_-4.png b/plugins/emc/example-graphs/emc_vnx_file_-4.png new file mode 100644 index 00000000..cf6afa1a Binary files /dev/null and b/plugins/emc/example-graphs/emc_vnx_file_-4.png differ diff --git a/plugins/emc/example-graphs/emc_vnx_file_-5.png b/plugins/emc/example-graphs/emc_vnx_file_-5.png new file mode 100644 index 00000000..fdcd1c1b Binary files /dev/null and b/plugins/emc/example-graphs/emc_vnx_file_-5.png differ diff --git a/plugins/emc/example-graphs/emc_vnx_file_-6.png b/plugins/emc/example-graphs/emc_vnx_file_-6.png new file mode 100644 index 00000000..6fba09cf Binary files /dev/null and b/plugins/emc/example-graphs/emc_vnx_file_-6.png differ diff --git a/plugins/emc/example-graphs/emc_vnx_file_-7.png b/plugins/emc/example-graphs/emc_vnx_file_-7.png new file mode 100644 index 00000000..f036dc02 Binary files /dev/null and b/plugins/emc/example-graphs/emc_vnx_file_-7.png differ diff --git a/plugins/emc/example-graphs/emc_vnx_file_-8.png b/plugins/emc/example-graphs/emc_vnx_file_-8.png new file mode 100644 index 00000000..593428d4 Binary files /dev/null and b/plugins/emc/example-graphs/emc_vnx_file_-8.png differ diff --git a/plugins/emc/example-graphs/emc_vnx_file_-9.png b/plugins/emc/example-graphs/emc_vnx_file_-9.png new file mode 100644 index 00000000..bdd0f9f4 Binary files /dev/null and b/plugins/emc/example-graphs/emc_vnx_file_-9.png differ diff --git a/plugins/emc/example-graphs/emc_vnx_file_-a.png b/plugins/emc/example-graphs/emc_vnx_file_-a.png new file mode 100644 index 00000000..b6e4cca3 Binary files /dev/null and b/plugins/emc/example-graphs/emc_vnx_file_-a.png differ diff --git a/plugins/emc/example-graphs/emc_vnx_file_-b.png b/plugins/emc/example-graphs/emc_vnx_file_-b.png new file mode 100644 index 00000000..dc4247d8 Binary files /dev/null and b/plugins/emc/example-graphs/emc_vnx_file_-b.png differ diff --git a/plugins/emc/example-graphs/emc_vnx_file_-c.png b/plugins/emc/example-graphs/emc_vnx_file_-c.png new file mode 100644 index 00000000..e4122740 Binary files /dev/null and b/plugins/emc/example-graphs/emc_vnx_file_-c.png differ diff --git a/plugins/emc/example-graphs/emc_vnx_file_-g.png b/plugins/emc/example-graphs/emc_vnx_file_-g.png new file mode 100644 index 00000000..021edcae Binary files /dev/null and b/plugins/emc/example-graphs/emc_vnx_file_-g.png differ diff --git a/plugins/emc/example-graphs/emc_vnx_file_-h.png b/plugins/emc/example-graphs/emc_vnx_file_-h.png new file mode 100644 index 00000000..67bb671c Binary files /dev/null and b/plugins/emc/example-graphs/emc_vnx_file_-h.png differ diff --git a/plugins/emc/example-graphs/emc_vnx_file_-i.png b/plugins/emc/example-graphs/emc_vnx_file_-i.png new file mode 100644 index 00000000..758fd66b Binary files /dev/null and b/plugins/emc/example-graphs/emc_vnx_file_-i.png differ diff --git a/plugins/emc/example-graphs/emc_vnx_file_-j.png b/plugins/emc/example-graphs/emc_vnx_file_-j.png new file mode 100644 index 00000000..c777e843 Binary files /dev/null and b/plugins/emc/example-graphs/emc_vnx_file_-j.png differ diff --git a/plugins/chassis/dell_fans b/plugins/fan/dell_fans similarity index 100% rename from plugins/chassis/dell_fans rename to plugins/fan/dell_fans diff --git a/plugins/system/ibmfan b/plugins/fan/ibmfan similarity index 97% rename from plugins/system/ibmfan rename to plugins/fan/ibmfan index aa65f7e0..95d147a2 100755 --- a/plugins/system/ibmfan +++ b/plugins/fan/ibmfan @@ -32,7 +32,7 @@ if [ "$1" = "config" ]; then echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel speed' echo 'graph_scale no' - echo 'graph_category system' + echo 'graph_category sensors' echo 'graph_info This graph shows the speed of the system fan.' echo 'fan.label speed' echo 'fan.info The current speed of the system fan.' diff --git a/plugins/fax/faxstat b/plugins/fax/faxstat index 7c0deefb..75f99366 100755 --- a/plugins/fax/faxstat +++ b/plugins/fax/faxstat @@ -32,7 +32,7 @@ JOBTYPES="Running Waiting Done" if [ "$1" = "config" ]; then echo 'graph_title Fax Jobs' echo 'graph_vlabel Jobs in Queue' - echo 'graph_category FAX' + echo 'graph_category other' for i in $JOBTYPES; do echo "$i.info Number of jobs in $i queue." echo "$i.label $i" diff --git a/plugins/firebird/firebird b/plugins/firebird/firebird index 972d741d..1e70bd79 100755 --- a/plugins/firebird/firebird +++ b/plugins/firebird/firebird @@ -22,7 +22,7 @@ graph_title Firebird Transaction Stats graph_order oldest_trans oldest_active oldest_snapshot next_transaction oldest_trans_gap1 oldest_trans_gap2 graph_args --base 1000 graph_scale no -graph_category firebird +graph_category db oldest_trans.label Oldest transaction - OIT oldest_trans.graph no oldest_active.label Oldest active diff --git a/plugins/fr24/fr24 b/plugins/fr24/fr24 new file mode 100755 index 00000000..67924623 --- /dev/null +++ b/plugins/fr24/fr24 @@ -0,0 +1,49 @@ +#!/bin/bash +# +: <<=cut + +=head1 NAME + +fr24 - Plugin to monitor your flightradar24.com feeder. + +=head1 APPLICABLE SYSTEMS + +All Linux systems. + +=head1 CONFIGURATION + +The following is default configuration: + + [fr24] + env.MONITOR http://192.168.1.1:8754/monitor.json + +Set the right URL according to your FR24 Feeder. + +=head1 VERSION + + 1 + +=head1 AUTHOR + +Benoît.S « Benpro » + +=head1 LICENSE + +WTFPL + +=cut + +MONITOR=${MONITOR:-http://192.168.1.1:8754/monitor.json} + +case $1 in + config) + echo "graph_title Number of planes in sight +graph_info Number of planes in sight with DVB-T receiver. +graph_category tv +graph_vlabel Number of planes +planes.label planes" + exit 0;; +esac + +planes=$(curl -qs "$MONITOR" | grep -Eo '"d11_map_size":"[0-9]+"' | grep -Eo '[0-9]+' | tail -1) +echo "planes.value $planes" diff --git a/plugins/other/freeradius b/plugins/freeradius/freeradius similarity index 98% rename from plugins/other/freeradius rename to plugins/freeradius/freeradius index bfc94470..ae5e3016 100755 --- a/plugins/other/freeradius +++ b/plugins/freeradius/freeradius @@ -46,7 +46,7 @@ if [ "$1" = "config" ]; then echo 'graph_period '${graph_period} fi echo 'graph_vlabel requests / ${graph_period}' - echo 'graph_category Other' + echo 'graph_category auth' echo 'requests.label Authentication requests' echo 'requests.info freeRADIUS authentication requests' diff --git a/plugins/other/freeradius_queue b/plugins/freeradius/freeradius_queue old mode 100644 new mode 100755 similarity index 98% rename from plugins/other/freeradius_queue rename to plugins/freeradius/freeradius_queue index 9010e18d..e4e8744d --- a/plugins/other/freeradius_queue +++ b/plugins/freeradius/freeradius_queue @@ -70,7 +70,7 @@ if [ "$1" = "config" ]; then echo 'graph_args --base 1000 -l 0 ' echo 'graph_period second' echo 'graph_vlabel requests / ${graph_period}' - echo 'graph_category Other' + echo 'graph_category auth' echo 'queue_len_internal.label Internal' echo 'queue_len_internal.info number of requests waiting in the internal queue' diff --git a/plugins/ftp/proftpd b/plugins/ftp/proftpd index aacae81b..f02c49fd 100755 --- a/plugins/ftp/proftpd +++ b/plugins/ftp/proftpd @@ -6,7 +6,7 @@ if [ "$1" = 'config' ]; then echo "graph_args --base 1000 -l 0" echo "graph_title Serveur FTP" - echo "graph_category Ftp" + echo "graph_category network" echo "graph_vlabel Stats Proftpd" echo "succes.label Login succes" echo "succes.draw AREA" diff --git a/plugins/ftp/proftpd_bytes b/plugins/ftp/proftpd_bytes index 85d3ef39..b34f38f7 100755 --- a/plugins/ftp/proftpd_bytes +++ b/plugins/ftp/proftpd_bytes @@ -22,7 +22,7 @@ mktemp -t $1 LOGFILE=${logfile:-/var/log/proftpd/xferlog} LOGTAIL=${logtail:-`which logtail`} -STATEFILE=/var/lib/munin/plugin-state/xferlog-bytes.offset +STATEFILE=$MUNIN_PLUGSTATE/xferlog-bytes.offset if [ "$1" = "autoconf" ]; then if [ -f "${LOGFILE}" -a -n "${LOGTAIL}" -a -x "${LOGTAIL}" ] ; then @@ -38,7 +38,7 @@ if [ "$1" = "config" ]; then echo 'graph_title FTP Server Bytes' echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel FTP Server Bytes' - echo 'graph_category FTP' + echo 'graph_category network' echo 'ftp_get.label Bytes GET' echo 'ftp_put.label Bytes PUT' exit 0 diff --git a/plugins/ftp/proftpd_count b/plugins/ftp/proftpd_count index aa9c9d3f..13f50eec 100755 --- a/plugins/ftp/proftpd_count +++ b/plugins/ftp/proftpd_count @@ -22,7 +22,7 @@ mktemp -t $1 LOGFILE=${logfile:-/var/log/proftpd/xferlog} LOGTAIL=${logtail:-`which logtail`} -STATEFILE=/var/lib/munin/plugin-state/xferlog-count.offset +STATEFILE=$MUNIN_PLUGSTATE/xferlog-count.offset if [ "$1" = "autoconf" ]; then if [ -f "${LOGFILE}" -a -n "${LOGTAIL}" -a -x "${LOGTAIL}" ] ; then @@ -38,7 +38,7 @@ if [ "$1" = "config" ]; then echo 'graph_title FTP Server Transfers' echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel FTP Server Transfers' - echo 'graph_category FTP' + echo 'graph_category network' echo 'ftp_get.label Files GET' echo 'ftp_put.label Files PUT' exit 0 diff --git a/plugins/ftp/pure-ftpd b/plugins/ftp/pure-ftpd index 3a2700ac..2a699778 100755 --- a/plugins/ftp/pure-ftpd +++ b/plugins/ftp/pure-ftpd @@ -59,7 +59,7 @@ if [ "$1" = "config" ]; then echo "graph_title $GRAPHTITLE" echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel connections' - echo 'graph_category ftp' + echo 'graph_category network' echo 'upload.label Upload' echo 'download.label Download' echo 'idle.label Idle' diff --git a/plugins/ftp/pure-ftpd-bw b/plugins/ftp/pure-ftpd-bw index 49db5ed3..aa9112d2 100755 --- a/plugins/ftp/pure-ftpd-bw +++ b/plugins/ftp/pure-ftpd-bw @@ -32,7 +32,7 @@ LOGFILE=/var/log/pure-ftpd/transfer.log LOGTAIL=$(which logtail) -OFFSET_FILE=/var/lib/munin/plugin-state/pure-ftpd-bw.offset +OFFSET_FILE=$MUNIN_PLUGSTATE/pure-ftpd-bw.offset if [ "$1" = "autoconf" ]; then @@ -54,7 +54,7 @@ if [ "$1" = "config" ]; then echo 'graph_title Pure Ftpd Bandwidth' echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel Datas sent / received' - echo 'graph_category ftp' + echo 'graph_category network' echo 'dl.label Bytes downloaded' echo 'ul.label Bytes uploaded' exit 0 diff --git a/plugins/ftp/pure-ftpd-logs b/plugins/ftp/pure-ftpd-logs index e4d9f881..2a0add13 100755 --- a/plugins/ftp/pure-ftpd-logs +++ b/plugins/ftp/pure-ftpd-logs @@ -22,7 +22,7 @@ LOGFILE=/var/log/syslog LOGTAIL=$(which logtail) -OFFSET_FILE=/var/lib/munin/plugin-state/pure-ftpd-conns.offset +OFFSET_FILE=$MUNIN_PLUGSTATE/pure-ftpd-conns.offset if [ "$1" = "autoconf" ]; then @@ -44,7 +44,7 @@ if [ "$1" = "config" ]; then echo 'graph_title Pure Ftpd Logs' echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel Connections number' - echo 'graph_category ftp' + echo 'graph_category network' echo 'nc.label new connection' echo 'al.label anonymous logged' echo 'ul.label auth user logged' diff --git a/plugins/ftp/pureftpd_count b/plugins/ftp/pureftpd_count index 93adb923..07f9dc8a 100755 --- a/plugins/ftp/pureftpd_count +++ b/plugins/ftp/pureftpd_count @@ -30,7 +30,7 @@ if [ "$1" = "config" ]; then echo 'graph_title FTP Server' echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel Daily FTP Operations' - echo 'graph_category FTP' + echo 'graph_category network' echo 'graph_period second' echo 'ftp_put.type GAUGE' echo 'ftp_get.type GAUGE' diff --git a/plugins/ftp/pureftpd_traffic b/plugins/ftp/pureftpd_traffic index f2eacc09..c9ec4c73 100755 --- a/plugins/ftp/pureftpd_traffic +++ b/plugins/ftp/pureftpd_traffic @@ -23,7 +23,7 @@ if ($ARGV[0] eq "test") { if ($ARGV[0] eq "config") { $out.="graph_title pureftpd traffic -graph_category ftp +graph_category network graph_info This graph shows pureftpd traffic. graph_vlabel Bytes get.label Get diff --git a/plugins/ftp/vsftpd b/plugins/ftp/vsftpd index 298c5025..d329552f 100755 --- a/plugins/ftp/vsftpd +++ b/plugins/ftp/vsftpd @@ -17,7 +17,7 @@ if [ "$1" = "config" ]; then graph_title vsftpd Server graph_args --base 1000 -l 0 graph_vlabel Requests -graph_category ftp +graph_category network ftp_c.label connections ftp_c.type DERIVE ftp_c.min 0 diff --git a/plugins/ftp/vsftpd-rel b/plugins/ftp/vsftpd-rel index 689ae60c..25ee99de 100755 --- a/plugins/ftp/vsftpd-rel +++ b/plugins/ftp/vsftpd-rel @@ -25,14 +25,13 @@ # env.logtail /usr/bin/logtail PROGNAME=vsftpd -STATEDIR=/var/lib/munin/plugin-state LOGFILE=${logfile:-/var/log/vsftpd.log} LOGTAIL=${logtail:-`which logtail`} -OFFSET=${STATEDIR}/${PROGNAME}.offset -STATE=${STATEDIR}/${PROGNAME}.state -PIVOT=${STATEDIR}/${PROGNAME}.pivot +OFFSET=${MUNIN_PLUGSTATE}/${PROGNAME}.offset +STATE=${MUNIN_PLUGSTATE}/${PROGNAME}.state +PIVOT=${MUNIN_PLUGSTATE}/${PROGNAME}.pivot install_ok() @@ -66,7 +65,7 @@ then echo 'system.type ABSOLUTE' echo 'graph_title Very Secure FTP Server' echo 'graph_vlabel Requests' - echo 'graph_category FTP' + echo 'graph_category network' echo 'ftp_conn.label connections' echo 'ftp_loginok.label successful logins' echo 'ftp_loginfail.label failed logins' diff --git a/plugins/funkytown/denon_x311_volume b/plugins/funkytown/denon_x311_volume index 1575a23a..4dfd2e98 100755 --- a/plugins/funkytown/denon_x311_volume +++ b/plugins/funkytown/denon_x311_volume @@ -32,7 +32,7 @@ BEGIN { if (ARGV[1] == "config") { print "graph_title Denon AVR-4311 Volume" - print "graph_category funkytown" + print "graph_category radio" print "volume.label Volume" print "volume.type GAUGE" exit 0 diff --git a/plugins/games/game b/plugins/games/game old mode 100644 new mode 100755 index 6f8a1a20..9cb182ad --- a/plugins/games/game +++ b/plugins/games/game @@ -30,7 +30,7 @@ $var = getenv('game_config_file'); $config = ($var) ? $var : "/etc/munin/munin-game.ini"; $var = getenv('game_state_file'); -$state = ($var) ? $var : "/var/lib/munin/plugin-state/game"; +$state = ($var) ? $var : (getenv('MUNIN_PLUGSTATE') . "/game"); function p($str) { // Quick function to print a string with an EOL on the end diff --git a/plugins/gearman/gearman_queue b/plugins/gearman/gearman_queue index 1c37baf1..3b8c9485 100755 --- a/plugins/gearman/gearman_queue +++ b/plugins/gearman/gearman_queue @@ -27,7 +27,7 @@ if (defined $ARGV[0] and $ARGV[0] eq 'config') { print "graph_title Gearman queue\n"; print "graph_args --base 1000\n"; print "graph_vlabel jobs\n"; - print "graph_category Gearman\n"; + print "graph_category cloud\n"; while ( my ($key, $value) = each(%$gearman) ) { my $param = $key."_"."queue"; print "$param.type GAUGE\n"; diff --git a/plugins/gearman/gearman_workers b/plugins/gearman/gearman_workers index 618db2dc..916ae4aa 100755 --- a/plugins/gearman/gearman_workers +++ b/plugins/gearman/gearman_workers @@ -35,7 +35,7 @@ if (defined $ARGV[0] and $ARGV[0] eq 'config') { print "graph_title Gearman workers\n"; print "graph_args --base 1000\n"; print "graph_vlabel workers\n"; - print "graph_category Gearman\n"; + print "graph_category cloud\n"; foreach my $key (@sorted) { my $param = $key."_"."total"; print "$param.type GAUGE\n"; diff --git a/plugins/geowebcache/geowebcache-bandwidth b/plugins/geowebcache/geowebcache-bandwidth index 689c99d9..d2f9c0ce 100755 --- a/plugins/geowebcache/geowebcache-bandwidth +++ b/plugins/geowebcache/geowebcache-bandwidth @@ -98,7 +98,7 @@ if(exists $ARGV[0] and $ARGV[0] eq "config") { print "graph_title GeoWebCache bandwidth\n"; print "graph_args --base 1024\n"; print "graph_vlabel bit/s\n"; - print "graph_category geowebcache\n"; + print "graph_category loadbalancer\n"; print "graph_info Bandwidth graph is an average on the last 60 seconds\n"; print "bandw.draw LINE1\n"; print "bandw.label bandwidth bit/s\n"; diff --git a/plugins/geowebcache/geowebcache-blankitems b/plugins/geowebcache/geowebcache-blankitems index 86577238..a3c80a2f 100755 --- a/plugins/geowebcache/geowebcache-blankitems +++ b/plugins/geowebcache/geowebcache-blankitems @@ -98,7 +98,7 @@ if(exists $ARGV[0] and $ARGV[0] eq "config") { print "graph_title GeoWebCache Percent of blank requests\n"; print "graph_args --base 1000\n"; print "graph_vlabel %\n"; - print "graph_category geowebcache\n"; + print "graph_category loadbalancer\n"; print "graph_info Blankidth graph is an average on the last 60 seconds\n"; print "blank.draw LINE1\n"; print "blank.label % of blank KML/HTML\n"; diff --git a/plugins/geowebcache/geowebcache-cache-hits-ratio b/plugins/geowebcache/geowebcache-cache-hits-ratio index 17253e4c..270c846e 100755 --- a/plugins/geowebcache/geowebcache-cache-hits-ratio +++ b/plugins/geowebcache/geowebcache-cache-hits-ratio @@ -98,7 +98,7 @@ if(exists $ARGV[0] and $ARGV[0] eq "config") { print "graph_title GeoWebCache cache hit ratio\n"; print "graph_args --base 1000\n"; print "graph_vlabel %\n"; - print "graph_category geowebcache\n"; + print "graph_category loadbalancer\n"; print "ratio.label percent\n"; print "ratio.type GAUGE\n"; print "ratio.max 100\n"; diff --git a/plugins/glance/glance_size_ b/plugins/glance/glance_size_ index 8a0bb565..4b3ba99f 100755 --- a/plugins/glance/glance_size_ +++ b/plugins/glance/glance_size_ @@ -13,24 +13,22 @@ # To show tenant name plugin must run as root # # Magic markers -#%# capabilities=autoconf suggest -#%# family=auto +# #%# capabilities=autoconf suggest +# #%# family=auto import sys import os -try: +try: from sqlalchemy.orm import joinedload import sqlalchemy.exc - + from glance.common.cfg import CommonConfigOpts from glance.registry.db import models from glance.registry.db.api import get_session, configure_db from keystone.common import utils - from keystone import config - from keystone import exception - from keystone import identity + from keystone import config, exception, identity except ImportError: successful_import = False @@ -40,7 +38,7 @@ else: def get_name_from_tenant(tenant): try: - KEYSTONE_CONF = config.CONF(config_files=[utils.find_config('keystone.conf')]) + config.CONF(config_files=[utils.find_config('keystone.conf')]) except: # keystone configuration can not be loaded, use id as name" return tenant @@ -53,65 +51,67 @@ def get_name_from_tenant(tenant): # keystone database can not be connected, use id as name" return tenant - if not tenant_info: + if not tenant_info: return tenant else: return tenant_info["name"] - + def load_conf(): - CONF = CommonConfigOpts(project="glance", prog="glance-registry") - CONF() + loaded_config = CommonConfigOpts(project="glance", prog="glance-registry") + loaded_config() # Hide missing logger warning message sys.stderr = open(os.devnull, 'w') - configure_db(CONF) + configure_db(loaded_config) sys.stderr = sys.__stderr__ def print_config(tenant): if tenant == "Global": - print 'graph_title Glance used size for all tenants' - print 'graph_info This graph shows the used size in glance for all tenants' + print('graph_title Glance used size for all tenants') + print('graph_info This graph shows the used size in glance for all tenants') else: - print 'graph_title Glance used size for tenant %s' % get_name_from_tenant(tenant) - print 'graph_info This graph shows the used size in glance for tenant %s' % tenant - print 'graph_vlabel Bytes' - print 'graph_args --base 1024 --lower-limit 0' - print 'graph_category glance' - print '%s.label %s' % (tenant, get_name_from_tenant(tenant)) - print '%s.draw LINE2' % tenant - print '%s.info %s MBytes' % (tenant, tenant) + print('graph_title Glance used size for tenant %s' % get_name_from_tenant(tenant)) + print('graph_info This graph shows the used size in glance for tenant %s' % tenant) + print('graph_vlabel Bytes') + print('graph_args --base 1024 --lower-limit 0') + print('graph_category cloud') + print('%s.label %s' % (tenant, get_name_from_tenant(tenant))) + print('%s.draw LINE2' % tenant) + print('%s.info %s MBytes' % (tenant, tenant)) + def request(**kwargs): session = get_session() try: - query = session.query(models.Image).\ - options(joinedload(models.Image.properties)).\ - options(joinedload(models.Image.members)) + query = session.query(models.Image).options(joinedload(models.Image.properties)).options( + joinedload(models.Image.members)) if kwargs: - query = query.filter_by(**kwargs) + query = query.filter_by(**kwargs) images = query.all() - except exc.NoResultFound: + except exception.NoResultFound: return [] return images + def print_suggest(): - print "Global" - print "\n".join(set( image["owner"] for image in request(deleted=False) )) + print("Global") + print("\n".join(set(image["owner"] for image in request(deleted=False)))) + def print_values(tenant): - if tenant != "Global" : + if tenant != "Global": images = request(deleted=False, owner=tenant) else: images = request(deleted=False) - total_size = sum([ image["size"] for image in images ]) - print '%s.value %s' % (tenant, total_size) + total_size = sum([image["size"] for image in images]) + print('%s.value %s' % (tenant, total_size)) if __name__ == '__main__': @@ -125,18 +125,17 @@ if __name__ == '__main__': load_conf() print_suggest() elif argv[1] == 'autoconf': - if not successful_import: - print 'no (failed import glance and/or sqlachemy module)' + if not successful_import: + print('no (failed import glance and/or sqlachemy module)') sys.exit(0) try: load_conf() get_session() except: - print 'no (failed to connect glance backend, check user)' + print('no (failed to connect glance backend, check user)') sys.exit(0) - print 'yes' + print('yes') elif successful_import: load_conf() print_values(tenant) - diff --git a/plugins/glance/glance_status b/plugins/glance/glance_status index 25bb25f5..95917a3c 100755 --- a/plugins/glance/glance_status +++ b/plugins/glance/glance_status @@ -44,7 +44,7 @@ def print_config(): print 'graph_title Glance images status' print 'graph_vlabel images' print 'graph_args --lower-limit 0' - print 'graph_category glance' + print 'graph_category cloud' print 'graph_scale no' print 'graph_info This graph show number of images by status' diff --git a/plugins/glassfish/glassfish_counters_ b/plugins/glassfish/glassfish_counters_ old mode 100644 new mode 100755 index d2189ae0..b962ec9e --- a/plugins/glassfish/glassfish_counters_ +++ b/plugins/glassfish/glassfish_counters_ @@ -66,7 +66,7 @@ case $1 in /dotted-name/ { myself = $NF print "graph_title GlassFish", myself print "graph_vlabel count" - print "graph_category glassfish" + print "graph_category appserver" print "graph_info this shows available counters from", myself next } diff --git a/plugins/google/google-rank b/plugins/google/google-rank index b2888e3c..00cafdba 100755 --- a/plugins/google/google-rank +++ b/plugins/google/google-rank @@ -55,7 +55,7 @@ if [ "$1" = "config" ]; then iLoop=1 echo 'graph_title Google page rank' echo 'graph_args --upper-limit 100 -l 0' - echo 'graph_category google' + echo 'graph_category search' echo 'graph_scale no' echo 'graph_info Google page rank for URLs & Words' diff --git a/plugins/google/googlecode b/plugins/google/googlecode index d0eaf3f4..fed13890 100755 --- a/plugins/google/googlecode +++ b/plugins/google/googlecode @@ -47,7 +47,7 @@ if [ "$1" = "config" ]; then echo "graph_title Number of downloads of $PROJECTNAME from Google Code " echo "graph_args --base 1000 --lower-limit 0" echo "graph_vlabel number of downloads" - echo "graph_category google" + echo "graph_category filetransfer" echo "graph_info This graph shows the number of downloads of $PROJECTNAME from Google Code." j=0 for (( i = 1 ; i < $Nfiles ; i=i+5 )) diff --git a/plugins/gpu/amd_gpu_ b/plugins/gpu/amd_gpu_ index 4f7095e0..85491c8d 100755 --- a/plugins/gpu/amd_gpu_ +++ b/plugins/gpu/amd_gpu_ @@ -109,14 +109,14 @@ if [ "$1" = "config" ]; then case $name in temp) echo 'graph_title GPU temperature' - echo 'graph_args -l 0 -u 120' + echo 'graph_args -l 20 -u 120' echo 'graph_vlabel Degrees (C)' - echo 'graph_category gpu' + echo 'graph_category sensors' echo "graph_info Temperature information for AMD GPUs" nGpusCounter=0 while [ $nGpusCounter -lt $nGpus ] do - gpuName=`echo "$nGpusOutput" | grep "* 0" | cut -f 1,3 --complement -d " "` + gpuName=`echo "$nGpusOutput" | grep "\ $nGpusCounter\.\ " | cut -f 3 -d "." | sed -r 's/^[0-9]+\ //'` echo "temp${nGpusCounter}.warning ${warning:-75}" echo "temp${nGpusCounter}.critical ${critical:-95}" echo "temp${nGpusCounter}.info Temperature information for $gpuName" @@ -138,12 +138,12 @@ if [ "$1" = "config" ]; then echo 'graph_title GPU clock' echo "graph_args -l 0 -u $maxclock" echo 'graph_vlabel MHz' - echo 'graph_category gpu' + echo 'graph_category sensors' echo "graph_info Core and memory clock info for AMD GPUs" nGpusCounter=0 while [ $nGpusCounter -lt $nGpus ] do - gpuName=`echo "$nGpusOutput" | grep "* 0" | cut -f 1,3 --complement -d " "` + gpuName=`echo "$nGpusOutput" | grep "\ $nGpusCounter\.\ " | cut -f 3 -d "." | sed -r 's/^[0-9]+\ //'` echo "memclock${nGpusCounter}.info Memory clock information for $gpuName" echo "memclock${nGpusCounter}.label Memory clock ($gpuName)" echo "coreclock${nGpusCounter}.info Core clock information for $gpuName" @@ -155,12 +155,12 @@ if [ "$1" = "config" ]; then echo 'graph_title GPU fan speed' echo 'graph_args -l 0 -u 100' echo 'graph_vlabel Percentage' - echo 'graph_category gpu' + echo 'graph_category sensors' echo "graph_info Fan speed of AMD GPUs" nGpusCounter=0 while [ $nGpusCounter -lt $nGpus ] do - gpuName=`echo "$nGpusOutput" | grep "* 0" | cut -f 1,3 --complement -d " "` + gpuName=`echo "$nGpusOutput" | grep "\ $nGpusCounter\.\ " | cut -f 3 -d "." | sed -r 's/^[0-9]+\ //'` echo "fan${nGpusCounter}.info Fan speed information for $gpuName" echo "fan${nGpusCounter}.label Fan speed ($gpuName)" : $(( nGpusCounter = $nGpusCounter + 1 )) @@ -170,12 +170,12 @@ if [ "$1" = "config" ]; then echo 'graph_title GPU load' echo 'graph_args -l 0 -u 100' echo 'graph_vlabel Percentage' - echo 'graph_category gpu' + echo 'graph_category sensors' echo "graph_info GPU load" nGpusCounter=0 while [ $nGpusCounter -lt $nGpus ] do - gpuName=`echo "$nGpusOutput" | grep "* 0" | cut -f 1,3 --complement -d " "` + gpuName=`echo "$nGpusOutput" | grep "\ $nGpusCounter\.\ " | cut -f 3 -d "." | sed -r 's/^[0-9]+\ //'` echo "load${nGpusCounter}.info Load information for $gpuName" echo "load${nGpusCounter}.label Load ($gpuName)" : $(( nGpusCounter = $nGpusCounter + 1 )) @@ -184,12 +184,12 @@ if [ "$1" = "config" ]; then vcore) echo 'graph_title GPU core voltage' echo 'graph_vlabel mV' - echo 'graph_category gpu' + echo 'graph_category sensors' echo "graph_info GPU core voltage" nGpusCounter=0 while [ $nGpusCounter -lt $nGpus ] do - gpuName=`echo "$nGpusOutput" | grep "* 0" | cut -f 1,3 --complement -d " "` + gpuName=`echo "$nGpusOutput" | grep "\ $nGpusCounter\.\ " | cut -f 3 -d "." | sed -r 's/^[0-9]+\ //'` echo "vcore${nGpusCounter}.info Vcore information for $gpuName" echo "vcore${nGpusCounter}.label Core voltage ($gpuName)" : $(( nGpusCounter = $nGpusCounter + 1 )) diff --git a/plugins/gpu/nvidia_gpu_ b/plugins/gpu/nvidia_gpu_ index 56741504..f50d1d26 100755 --- a/plugins/gpu/nvidia_gpu_ +++ b/plugins/gpu/nvidia_gpu_ @@ -37,8 +37,7 @@ C =item * -Add support for specific professional GPU features such as number of compute -processes, clocks, power draw, utilization, and so on. +Add support for specific professional GPU features such as number of compute processes, clocks and so on. =item * @@ -64,7 +63,7 @@ faken@fakenmc.com =cut # Determine name of parameter to monitor -name=`basename $0 | sed 's/^nvidia_gpu_//g'` +name=$(basename "$0" | sed 's/^nvidia_gpu_//g') # Get location of nvidia-smi executable or use default nvSmiExec=${smiexec:-'/usr/bin/nvidia-smi'} @@ -72,7 +71,7 @@ nvSmiExec=${smiexec:-'/usr/bin/nvidia-smi'} # Check if autoconf was requested if [ "$1" = "autoconf" ]; then # Autoconf only returns yes if nvidia-smi exists and is executable - if [ -x $nvSmiExec ]; then + if [ -x "$nvSmiExec" ]; then echo yes exit 0 else @@ -86,80 +85,109 @@ if [ "$1" = "suggest" ]; then echo "temp" echo "mem" echo "fan" + echo "power" + echo "utilization" exit 0 fi # Get number of GPUs -nGpusOutput=`$nvSmiExec -L` -nGpus=`echo "$nGpusOutput" | wc -l` -if [ $nGpus -eq 0 ]; then +nGpusOutput=$("$nvSmiExec" -L) +nGpus=$(echo "$nGpusOutput" | wc -l) +if [ "$nGpus" -eq 0 ]; then # Exit if no GPUs found echo "No NVIDIA GPUs detected. Exiting." exit 1 fi # Get full output from nvidia-smi -smiOutput=`$nvSmiExec -q` +smiOutput=$("$nvSmiExec" -q) # Check if config was requested if [ "$1" = "config" ]; then # Get driver version - driverVersion=`nvidia-smi -q | grep "Driver Version" | cut -d : -f 2 | tr -d ' '` + driverVersion=$(echo "$smiOutput" | grep "Driver Version" | cut -d : -f 2 | tr -d ' ') # Configure graph depending on what which quantity will be plotted case $name in temp) echo 'graph_title GPU temperature' echo 'graph_args -l 0 -u 120' - echo 'graph_vlabel Degrees (C)' - echo 'graph_category gpu' + echo 'graph_vlabel degrees Celsius' + echo 'graph_category sensors' echo "graph_info Temperature information for NVIDIA GPUs using driver version $driverVersion" nGpusCounter=0 - while [ $nGpusCounter -lt $nGpus ] + while [ $nGpusCounter -lt "$nGpus" ] do - gpuName=`echo "$nGpusOutput" | sed -n $(( $nGpusCounter + 1 ))p | cut -d \( -f 1` - echo "temp${nGpusCounter}.warning ${warning:-75}" - echo "temp${nGpusCounter}.critical ${critical:-95}" - echo "temp${nGpusCounter}.info Temperature information for $gpuName" - : $(( nGpusCounter = $nGpusCounter + 1 )) - done + gpuName=$(echo "$nGpusOutput" | sed -n $((nGpusCounter+1))p | cut -d \( -f 1) + echo "${name}${nGpusCounter}.warning ${warning:-75}" + echo "${name}${nGpusCounter}.critical ${critical:-95}" + echo "${name}${nGpusCounter}.info Temperature information for $gpuName" + : $((nGpusCounter=nGpusCounter+1)) + done ;; mem) # First determine total memory of each GPU... - gpusTotalMemOutput=`echo "$smiOutput" | grep -v BAR1 | grep -A 3 "Memory Usage" | grep "Total" | cut -d : -f 2 | tr -d ' '` + gpusTotalMemOutput=$(echo "$smiOutput" | grep -v BAR1 | grep -A 3 "Memory Usage" | grep "Total" | cut -d : -f 2 | tr -d ' ') gpusTotalMem='' nGpusCounter=0 - while [ $nGpusCounter -lt $nGpus ] + while [ $nGpusCounter -lt "$nGpus" ] do - gpuName=`echo "$nGpusOutput" | sed -n $(( $nGpusCounter + 1 ))p | cut -d \( -f 1` - echo "mem${nGpusCounter}.info Memory information for $gpuName" - gpuMem=`echo "$gpusTotalMemOutput"| sed -n $(( $nGpusCounter + 1 ))p` + gpuName=$(echo "$nGpusOutput" | sed -n $((nGpusCounter+1))p | cut -d \( -f 1) + echo "${name}${nGpusCounter}.info Memory information for $gpuName" + gpuMem=$(echo "$gpusTotalMemOutput"| sed -n $((nGpusCounter+1))p) gpusTotalMem="${gpusTotalMem}${gpuMem} for GPU ${nGpusCounter}" - : $(( nGpusCounter = $nGpusCounter + 1 )) - if [ $nGpusCounter -lt $nGpus ]; then + : $((nGpusCounter=nGpusCounter+1)) + if [ "$nGpusCounter" -lt "$nGpus" ]; then gpusTotalMem="${gpusTotalMem}, " fi done # ...then output config data. echo 'graph_title GPU memory usage' echo 'graph_args -l 0 -u 100' - echo 'graph_vlabel Percentage' - echo 'graph_category gpu' + echo 'graph_vlabel %' + echo 'graph_category memory' echo "graph_info FB Memory usage for NVIDIA GPUs using driver version $driverVersion (total memory is $gpusTotalMem)" ;; fan) echo 'graph_title GPU fan speed' echo 'graph_args -l 0 -u 100' - echo 'graph_vlabel Percentage' - echo 'graph_category gpu' + echo 'graph_vlabel %' + echo 'graph_category sensors' echo "graph_info Fan speed of NVIDIA GPUs using driver version $driverVersion" nGpusCounter=0 - while [ $nGpusCounter -lt $nGpus ] + while [ $nGpusCounter -lt "$nGpus" ] do - gpuName=`echo "$nGpusOutput" | sed -n $(( $nGpusCounter + 1 ))p | cut -d \( -f 1` - echo "fan${nGpusCounter}.info Fan information for $gpuName" - : $(( nGpusCounter = $nGpusCounter + 1 )) + gpuName=$(echo "$nGpusOutput" | sed -n $((nGpusCounter+1))p | cut -d \( -f 1) + echo "${name}${nGpusCounter}.info Fan information for $gpuName" + : $((nGpusCounter=nGpusCounter+1)) + done + ;; + power) + echo 'graph_title GPU power consumption' + echo 'graph_vlabel Watt' + echo 'graph_category sensors' + echo "graph_info power consumption of NVIDIA GPUs using driver version $driverVersion" + nGpusCounter=0 + while [ $nGpusCounter -lt "$nGpus" ] + do + gpuName=$(echo "$nGpusOutput" | sed -n $((nGpusCounter+1))p | cut -d \( -f 1) + echo "${name}${nGpusCounter}.info power consumption of $gpuName" + : $((nGpusCounter=nGpusCounter+1)) + done + ;; + utilization) + echo 'graph_title GPU utilization' + echo 'graph_args -l 0 -u 100' + echo 'graph_vlabel %' + echo 'graph_category system' + echo "graph_info GPU utilization of NVIDIA GPUs using driver version $driverVersion" + nGpusCounter=0 + while [ $nGpusCounter -lt "$nGpus" ] + do + gpuName=$(echo "$nGpusOutput" | sed -n $((nGpusCounter+1))p | cut -d \( -f 1) + echo "${name}${nGpusCounter}.info GPU utilization information for $gpuName" + : $((nGpusCounter=nGpusCounter+1)) done ;; *) @@ -171,11 +199,11 @@ if [ "$1" = "config" ]; then # Common stuff for all quantities nGpusCounter=0 - while [ $nGpusCounter -lt $nGpus ] + while [ $nGpusCounter -lt "$nGpus" ] do - gpuName=`echo "$nGpusOutput" | sed -n $(( $nGpusCounter + 1 ))p | cut -d \( -f 1` + gpuName=$(echo "$nGpusOutput" | sed -n $((nGpusCounter+1))p | cut -d \( -f 1) echo "${name}${nGpusCounter}.label $gpuName" - : $(( nGpusCounter = $nGpusCounter + 1 )) + : $((nGpusCounter=nGpusCounter+1)) #print_warning $name #print_critical $name done @@ -186,24 +214,30 @@ fi # Get requested value case $name in temp) - valueGpus=`echo "$smiOutput" | grep -A 1 "Temperature" | grep -i "Gpu" | cut -d : -f 2 | cut -d ' ' -f 2` + valueGpus=$(echo "$smiOutput" | grep -A 1 "Temperature" | grep -i "Gpu" | cut -d : -f 2 | cut -d ' ' -f 2) ;; mem) - totalMemGpus=`echo "$smiOutput" | grep -v BAR1 | grep -A 3 "Memory Usage" | grep "Total" | cut -d : -f 2 | cut -d ' ' -f 2` - usedMemGpus=`echo "$smiOutput" | grep -v BAR1 | grep -A 3 "Memory Usage" | grep "Used" | cut -d : -f 2 | cut -d ' ' -f 2` + totalMemGpus=$(echo "$smiOutput" | grep -v BAR1 | grep -A 3 "Memory Usage" | grep "Total" | cut -d : -f 2 | cut -d ' ' -f 2) + usedMemGpus=$(echo "$smiOutput" | grep -v BAR1 | grep -A 3 "Memory Usage" | grep "Used" | cut -d : -f 2 | cut -d ' ' -f 2) valueGpus='' nGpusCounter=0 - while [ $nGpusCounter -lt $nGpus ] + while [ $nGpusCounter -lt "$nGpus" ] do - totalMemGpu=`echo "$totalMemGpus" | sed -n $(( $nGpusCounter + 1 ))p` - usedMemGpu=`echo "$usedMemGpus" | sed -n $(( $nGpusCounter + 1 ))p` - percentMemUsed=$(( $usedMemGpu * 100 / $totalMemGpu )) + totalMemGpu=$(echo "$totalMemGpus" | sed -n $((nGpusCounter+1))p) + usedMemGpu=$(echo "$usedMemGpus" | sed -n $((nGpusCounter+1))p) + percentMemUsed=$((usedMemGpu*100/totalMemGpu)) valueGpus="${valueGpus}${percentMemUsed}"$'\n' - : $(( nGpusCounter = $nGpusCounter + 1 )) + : $((nGpusCounter=nGpusCounter+1)) done ;; fan) - valueGpus=`echo "$smiOutput" | grep "Fan Speed" | cut -d ':' -f 2 | cut -d ' ' -f 2` + valueGpus=$(echo "$smiOutput" | grep "Fan Speed" | cut -d ':' -f 2 | cut -d ' ' -f 2) + ;; + power) + valueGpus=$(echo "$smiOutput" | grep "Power Draw" | cut -d ':' -f 2 | cut -d ' ' -f 2) + ;; + utilization) + valueGpus=$(echo "$smiOutput" | grep "Gpu" | cut -d ':' -f 2 | cut -d ' ' -f 2) ;; *) echo "Can't run without a proper symlink. Exiting." @@ -215,12 +249,9 @@ case $name in # Print requested value nGpusCounter=0 -while [ $nGpusCounter -lt $nGpus ] +while [ $nGpusCounter -lt "$nGpus" ] do - value=`echo "$valueGpus" | sed -n $(( $nGpusCounter + 1 ))p` + value=$(echo "$valueGpus" | sed -n $((nGpusCounter+1))p) echo "${name}${nGpusCounter}.value $value" - : $(( nGpusCounter = $nGpusCounter + 1 )) + : $((nGpusCounter=nGpusCounter+1)) done - - - diff --git a/plugins/gpu/nvidia_smi_ b/plugins/gpu/nvidia_smi_ index f57b763d..f5395d5e 100755 --- a/plugins/gpu/nvidia_smi_ +++ b/plugins/gpu/nvidia_smi_ @@ -75,7 +75,7 @@ for (my $i = 0; $i < $gpuCount; $i++) print "graph_title GPU\n"; print "graph_args --upper-limit 120 -l 0\n"; print "graph_vlabel Percent or Degrees C\n"; - print "graph_category GPU\n"; + print "graph_category sensors\n"; print "graph_info Information for NVIDIA GPUs using driver version $driverVersion\n"; } diff --git a/plugins/snmp/snmp__gwia_bytes_ b/plugins/groupwise/snmp__gwia_bytes_ similarity index 98% rename from plugins/snmp/snmp__gwia_bytes_ rename to plugins/groupwise/snmp__gwia_bytes_ index 32f94cbb..8f2f3782 100755 --- a/plugins/snmp/snmp__gwia_bytes_ +++ b/plugins/groupwise/snmp__gwia_bytes_ @@ -91,7 +91,6 @@ my $pos = $ENV{pos} || 0; my $response; -my $GRAPH_CATEGORY = "Groupwise"; my $GRAPH_PERIOD = "minute"; my $GRAPH_VLABEL = "bytes per $GRAPH_PERIOD in(-) / out(+)"; my $BYTES_LABEL='Bytes'; @@ -134,7 +133,7 @@ if (defined $ARGV[0] and $ARGV[0] eq "config") my $gwname = &get_single ($session, "1.3.6.1.4.1.23.2.70.1.1.$pos"); # gwiaGatewayName # output to munin print "host_name $host -graph_category $GRAPH_CATEGORY +graph_category mail graph_args --base 1000 graph_period $GRAPH_PERIOD graph_title GWIA byte load ($gwname) diff --git a/plugins/snmp/snmp__gwia_msgs_ b/plugins/groupwise/snmp__gwia_msgs_ similarity index 99% rename from plugins/snmp/snmp__gwia_msgs_ rename to plugins/groupwise/snmp__gwia_msgs_ index 2f2a68d7..4fb7d9ec 100755 --- a/plugins/snmp/snmp__gwia_msgs_ +++ b/plugins/groupwise/snmp__gwia_msgs_ @@ -96,7 +96,6 @@ my $pos = $ENV{pos} || 0; my $response; # fix values -my $GRAPH_CATEGORY = "Groupwise"; my $GRAPH_PERIOD = "minute"; my $GRAPH_VLABEL = "messages per $GRAPH_PERIOD in(-) / out(+)"; my $MSGS_LABEL='Messages'; @@ -147,7 +146,7 @@ if (defined $ARGV[0] and $ARGV[0] eq "config") # output to munin print "host_name $host -graph_category Groupwise +graph_category mail graph_args --base 1000 graph_period $GRAPH_PERIOD graph_title GWIA msg load ($gwname) diff --git a/plugins/snmp/snmp__gwmta_msgs_ b/plugins/groupwise/snmp__gwmta_msgs_ similarity index 99% rename from plugins/snmp/snmp__gwmta_msgs_ rename to plugins/groupwise/snmp__gwmta_msgs_ index d4a298f1..ee74f06a 100755 --- a/plugins/snmp/snmp__gwmta_msgs_ +++ b/plugins/groupwise/snmp__gwmta_msgs_ @@ -93,7 +93,6 @@ my $pos = $ENV{pos} || 1; my $response; # fix values -my $GRAPH_CATEGORY = "Groupwise"; my $GRAPH_PERIOD = "minute"; my $GRAPH_VLABEL = "number of messages / 10 min"; my $ROUTED_LABEL="Routed"; @@ -143,7 +142,7 @@ if (defined $ARGV[0] and $ARGV[0] eq "config") # output to munin print "host_name $host -graph_category Groupwise +graph_category mail graph_args --base 1000 graph_period $GRAPH_PERIOD graph_title GWMTA load ($domain) diff --git a/plugins/snmp/snmp__gwpoa_ b/plugins/groupwise/snmp__gwpoa_ similarity index 98% rename from plugins/snmp/snmp__gwpoa_ rename to plugins/groupwise/snmp__gwpoa_ index 01bd1d65..78051b3f 100755 --- a/plugins/snmp/snmp__gwpoa_ +++ b/plugins/groupwise/snmp__gwpoa_ @@ -100,7 +100,6 @@ my $pos = $ENV{pos} || 1; my $response; -my $GRAPH_CATEGORY = "Groupwise"; my $GRAPH_PERIOD = "minute"; my $GRAPH_VLABEL = "messages per $GRAPH_PERIOD"; my $TOTAL_LABEL = "TotalMsgs"; @@ -151,7 +150,7 @@ if (defined $ARGV[0] and $ARGV[0] eq "config") # output to munin print "host_name $host -graph_category $GRAPH_CATEGORY +graph_category mail graph_args --base 1000 graph_period $GRAPH_PERIOD graph_title GWPOA load ($poname) diff --git a/plugins/gunicorn/gunicorn_memory_status b/plugins/gunicorn/gunicorn_memory_status index 4cdbb58b..630af52e 100755 --- a/plugins/gunicorn/gunicorn_memory_status +++ b/plugins/gunicorn/gunicorn_memory_status @@ -86,7 +86,7 @@ def print_config(): print graph_title print "graph_args --base 1024 -l 0" print "graph_vlabel Megabytes" - print "graph_category gunicorn" + print "graph_category appserver" print "total_memory.label Total Memory" if __name__ == "__main__": diff --git a/plugins/gunicorn/gunicorn_status b/plugins/gunicorn/gunicorn_status index 78cd48d8..471b291d 100755 --- a/plugins/gunicorn/gunicorn_status +++ b/plugins/gunicorn/gunicorn_status @@ -101,7 +101,7 @@ def print_config(): print graph_title print "graph_args -l 0" print "graph_vlabel Number of workers" - print "graph_category gunicorn" + print "graph_category appserver" print "total_workers.label Total Workers" print "idle_workers.label Idle Workers" diff --git a/plugins/haproxy/haproxy-bytes b/plugins/haproxy/haproxy-bytes index b719c95e..303a849e 100755 --- a/plugins/haproxy/haproxy-bytes +++ b/plugins/haproxy/haproxy-bytes @@ -30,7 +30,7 @@ graph_vlabel=${title} case $1 in config) cat <> /etc/munin/urls.txt -# $ echo "http://www.intrafish.no/" >> /etc/munin/urls.txt -# -# 3. Add a cron job running the plugin with cron as the argument: -# */15 * * * * /usr/share/munin/plugins/http_load_ cron -# should be the user that has write permission to -# the $cachedir directory set below. Set the intervals to -# whatever you want. -# -# For verbose output (for debugging) you can do: -# sudo -u /usr/share/munin/plugins/http_load_ cron verbose -# -# 4. Run munin-node-configure --suggest --shell and run the symlink -# commands manually to update the munin-node plugin list. -# -# (5. If you want to change the filter which the plugin uses to select which -# tags to follow in a web page, edit the subroutine called "filter" below.) -# -# Add a new url to monitor: -# 1. Add a new line in /etc/munin/urls.txt with the full URL, i.e.: -# $ echo "http://www.linpro.no/" >> /etc/munin/http_load_urls.txt -# -# 2. Run munin-node-configure --suggest --shell and manually -# add the new symlink(s) -# -# 3. /etc/init.d/munin-node restart -# -# Remove a url from monitoring: -# 1. Remove it from /etc/munin/http_load_urls.txt -# -# 2. Remove ${cachedir}/http_load_* -# -# 3. Remove /etc/munin/plugins/http_load_* -# -# 4. /etc/init.d/munin-node restart -# -##### -# -# Todo: -# * Add support for forking to simulate real browsers -# * Use checksums as fieldnames -# -# $Id: $ -# -# Magic markers: -#%# family=auto -#%# capabilities=autoconf suggest + +=head1 NAME + +http_load_ Munin multigraph plugin to monitor websites's HTTP responses and performance + +=head1 DESCRIPTION + +The purpose of this plugin is to monitor several properties of a web page. +All measurements are done for the complete web page, including images, css +and other content a standard browser would download automatically. + +This version supports monitoring: + - loadtime: total time to download a complete web page (using serial GET requests) + - size: total size of a web page + - response: different response codes (200, 404, 500, etc) + - tags: HTML tags (img src, a href, etc) + - type: content types (image/png, text/css/, etc) + - elements: source of elements loaded by the web page + +=head1 REQUIREMENTS + + - The server running this plugin must be allowed to connect to the web + server(s) you are going to monitor. + - Some perl modules: + Time::HiRes, LWP::UserAgent, HTML::LinkExtor, LWP::ConnCache + +=head1 CONFIGURATION + +=head2 INITIAL SETUP + +1. Copy this file to /usr/share/munin/plugins/ + +2. Create a file (/etc/munin/http_load_urls.txt) with one + full url per line, as many as you want, i.e.: + $ echo "http://www.dn.no/" >> /etc/munin/urls.txt + $ echo "http://www.intrafish.no/" >> /etc/munin/urls.txt + +3. Add a cron job running the plugin with cron as the argument: + */15 * * * * /usr/sbin/munin-run http_load__loadtime cron + should be the user that has write permission to the $cachedir + directory set below. should be any of the configured sites (all + sites will get updated), likewise, you should replace loadtime by any + metric that is enabled for that site (all metrics will get updated). + Set the intervals to whatever you want. + + For verbose output (for debugging) you can do: + sudo -u /usr/share/munin/plugins/http_load_ cron verbose + +4. Run munin-node-configure --suggest --shell and run the symlink + commands manually to update the munin-node plugin list.xi + +5. If you want to change the filter which the plugin uses to select which + tags to follow in a web page, edit the subroutine called "filter" below.) + +=head2 SPECIFY URLS TO MONITOR + +1. Add a new line in /etc/munin/urls.txt with the full URL, i.e.: + $ echo "http://www.linpro.no/" >> /etc/munin/http_load_urls.txt + +2. Run munin-node-configure --suggest --shell and manually + add the new symlink(s) + +3. /etc/init.d/munin-node restart + +=head2 REMOVE A URL + +1. Remove it from /etc/munin/http_load_urls.txt + +2. Remove ${cachedir}/http_load_* + +3. Remove /etc/munin/plugins/http_load_* + +4. /etc/init.d/munin-node restart + +=head2 SINGLE GRAPH SUPPORT + +The default behaviour is the multigraph mode: only the loadtime will be shown +on the Munin summary page. The graphs there are linked to a second-level +summary page that list all other metrics. It is also possible to create +single graphs, that would show immediately on the summary page, by using +symlinks with a different name, postfixed with the name of the metric: + + - http_load_hostname: multigraph (default) + - http_load_hostname_loadtime: loadtime only + - http_load_hostname_size: total page size + - http_load_hostname_response: response code + - http_load_hostname_tags: HTML tags summary + - http_load_hostname_type: Content-Types + - http_load_hostname_elements: source site of the loaded elements + +Note that hostname is not the FQDN of the host, but rather the one given when +running munin-node-configure --suggest --shell and run the symlink + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf suggest + +=head1 TODO + + - Specify URLs from a standard Munin plugins configuration file (e.g., env.urls) + - Add support for forking to simulate real browsers + +=head1 AUTHORS + + - Espen Braastad / Linpro AS , initial implementation + - Olivier Mehani , multigraph support + +=cut use strict; use Time::HiRes qw( gettimeofday tv_interval ); @@ -87,14 +117,13 @@ use HTML::LinkExtor; use LWP::ConnCache; my $url_file="/etc/munin/http_load_urls.txt"; -my $cachedir="/var/lib/munin/plugin-state"; +my $cachedir=$ENV{MUNIN_PLUGSTATE}; -my $debug=0; +my $debug=$ENV{MUNIN_DEBUG}; my $timeout=10; my $max_redirects=10; my $scriptname="http_load_"; -my $category="network"; # The munin graph category -my $useragent="Mozilla/5.0"; +my $useragent="Mozilla/5.0 (Munin; $scriptname)"; # Function to read the $url_file and return the contents in a hash sub read_urls{ @@ -145,6 +174,13 @@ sub filter{ # status=1 => do download (default) # status=0 => do not download + # For links, the 'rel' is more relevant that the 'src' attribute + if("$tag" =~ /^link/){ + $status=0; + if("$tag" =~ /stylesheet$/){ + $status=1; + } + } if("$tag" eq "form action"){ $status=0; } @@ -154,6 +190,9 @@ sub filter{ if("$tag" eq "area href"){ $status=0; } + if("$tag" eq "meta content"){ + $status=0; + } return $status; } @@ -161,7 +200,6 @@ sub filter{ sub get_cache_file_name{ my $scriptname=$_[0]; my $id=$_[1]; - my $type=$_[2]; my $file=""; $file = $scriptname . $id . ".cache"; @@ -170,14 +208,23 @@ sub get_cache_file_name{ return $file; } -# Get fieldname (making sure it is munin "compatible" as a fieldname) +# Get fieldname (making sure it is munin-1.0 "compatible" as a fieldname) # 1. Remove all non-word characters from a string) # 2. Make sure it has maximum 19 characters +# 2.1 If not, truncate the host part, while keeping anything after an underscore (e.g., HTTP response status) sub get_fieldname{ my $url=$_[0]; $url =~ s/\W//g; if(length($url) > 19){ - $url = substr($url, 0, 19); + $url =~ s/(\S+)_(\S+)/ /g; + my $host = $1; + my $info = $2; + my $suffixlength = length($info) + 1; + if ($suffixlength > 1) { + $url = substr($host, 0, 19 - $suffixlength) . '_' . $info; + } else { + $url = substr($url, 0, 19); + } } return $url; } @@ -189,6 +236,340 @@ sub get_id{ return $url; } +sub graph_title_config{ + my $id = $_[0]; + my %urls = %{$_[1]}; + my $type = $_[2]; + + print "graph_title $urls{$id} ${type}\n"; + print "graph_args -l 0 --base 1000\n"; + print "graph_category webserver\n"; +} + +sub size_config{ + my $id = $_[0]; + my %urls = %{$_[1]}; + my %cache = %{$_[2]}; + + my $count = 0; + + graph_title_config($id, \%urls, "size"); + + print "graph_vlabel Bytes\n"; + print "graph_total Total\n"; + print "graph_info This graph is generated by a set of serial GETs to calculate the total size of $urls{$id}.\n"; + + if(keys(%cache)>0){ + for my $key ( sort reverse keys %cache ){ + my $value=$cache{$key}; + + if($key =~ m/^size_(\S+)$/){ + my $host=$1; + my $value=$value; + + my $name=$1; + $name=get_fieldname($name); + + print "$name.label from $host\n"; + print "$name.min 0\n"; + print "$name.max 20000000\n"; + if($count eq 0){ + print "$name.draw AREA\n"; + } else { + print "$name.draw STACK\n"; + } + $count+=1; + } + } + } +} + +sub loadtime_config{ + my $id = $_[0]; + my %urls = %{$_[1]}; + my %cache = %{$_[2]}; + + my $count = 0; + + graph_title_config($id, \%urls, "loadtime"); + + print "graph_vlabel Seconds\n"; + print "graph_total Total\n"; + print "graph_info This graph is generated by a set of serial GETs to calculate the total time to load $urls{$id}. "; + print "Note that browsers usually fork() the GET requests, resulting in a shorter total loading time.\n"; + + if(keys(%cache)>0){ + for my $key ( sort reverse keys %cache ){ + my $value=$cache{$key}; + + if($key =~ m/^loadtime_(\S+)$/){ + my $host=$1; + my $value=$value; + + my $name=$1; + $name=get_fieldname($name); + + print "$name.label from $host\n"; + print "$name.min 0\n"; + print "$name.max 400\n"; + if($count eq 0){ + print "$name.draw AREA\n"; + } else { + print "$name.draw STACK\n"; + } + $count+=1; + } + } + } +} + +sub elements_config{ + my $id = $_[0]; + my %urls = %{$_[1]}; + my %cache = %{$_[2]}; + + my $count = 0; + + graph_title_config($id, \%urls, "elements"); + + print "graph_vlabel Number of elements\n"; + print "graph_total Total\n"; + print "graph_info This graph is generated by a set of serial GETs to count the number of elements (images, CSS files, etc) from $urls{$id}.\n"; + + if(keys(%cache)>0){ + for my $key ( sort reverse keys %cache ){ + my $value=$cache{$key}; + + if($key =~ m/^elements_(\S+)$/){ + my $host=$1; + my $value=$value; + + my $name=$1; + $name=get_fieldname($name); + + print "$name.label from $host\n"; + print "$name.min 0\n"; + print "$name.max 10000\n"; + if($count eq 0){ + print "$name.draw AREA\n"; + } else { + print "$name.draw STACK\n"; + } + $count+=1; + } + } + } +} + +sub response_config{ + my $id = $_[0]; + my %urls = %{$_[1]}; + my %cache = %{$_[2]}; + + my $count = 0; + + graph_title_config($id, \%urls, "response"); + + print "graph_vlabel Server response code count\n"; + print "graph_total Total\n"; + print "graph_info This graph is generated by a set of serial GETs to visualize the server response codes received while loading $urls{$id}.\n"; + + if(keys(%cache)>0){ + for my $key ( sort reverse keys %cache ){ + my $value=$cache{$key}; + + if($key =~ m/^response_(\S+)$/){ + my $host=$1; + my $value=$value; + + my $name=$1; + $name=get_fieldname($name); + + $host =~ s/\_/ /g; + $host =~ s/(\S+)\s(\d+)/ /g; + $host=$1; + my $code=$2; + + print "$name.label $host ($code)\n"; + print "$name.min 0\n"; + print "$name.max 10000\n"; + if($count eq 0){ + print "$name.draw AREA\n"; + } else { + print "$name.draw STACK\n"; + } + $count+=1; + } + } + } +} + +sub type_config{ + my $id = $_[0]; + my %urls = %{$_[1]}; + my %cache = %{$_[2]}; + + my $count = 0; + + graph_title_config($id, \%urls, "type"); + + print "graph_vlabel Content type count\n"; + print "graph_total Total\n"; + print "graph_info This graph is generated by a set of serial GETs to visualize the different content types $urls{$id} consists of.\n"; + + if(keys(%cache)>0){ + for my $key ( sort reverse keys %cache ){ + my $value=$cache{$key}; + + if($key =~ m/^type_(\S+)$/){ + my $type=$1; + my $value=$value; + + my $name=$1; + $name=get_fieldname($name); + + #$host =~ s/\_/ /g; + #$host =~ s/(\S+)\s(\S+)/ /g; + #$host=$1; + #my $type=$2; + + print "$name.label $type\n"; + print "$name.min 0\n"; + print "$name.max 100000\n"; + if($count eq 0){ + print "$name.draw AREA\n"; + } else { + print "$name.draw STACK\n"; + } + $count+=1; + } + } + } +} + +sub tags_config{ + my $id = $_[0]; + my %urls = %{$_[1]}; + my %cache = %{$_[2]}; + + my $count = 0; + + graph_title_config($id, \%urls, "tags"); + + print "graph_vlabel HTML tag count\n"; + print "graph_total Total\n"; + print "graph_info This graph is generated by a set of serial GETs to visualize the different tags $urls{$id} consists of.\n"; + + if(keys(%cache)>0){ + for my $key ( sort reverse keys %cache ){ + my $value=$cache{$key}; + + if($key =~ m/^tags_(\S+)$/){ + my $host=$1; + my $value=$value; + + my $name=$1; + $name=get_fieldname($name); + + $host =~ s/\W/ /g; + + print "$name.label $host\n"; + print "$name.min 0\n"; + print "$name.max 100000\n"; + if($count eq 0){ + print "$name.draw AREA\n"; + } else { + print "$name.draw STACK\n"; + } + $count+=1; + } + } + } +} + +sub cache_values{ + my %cache = %{$_[0]}; + my $type = $_[1]; + + if(keys(%cache)>0){ + for my $key ( sort keys %cache ){ + my $value=$cache{$key}; + if($key =~ m/^([A-Za-z]+)\_(\S+)$/){ + my $name=$2; + + if ($1 eq $type){ + $name=get_fieldname($name); + print $name . ".value " . $value . "\n"; + } + } elsif(m/^(\S+)\s+(\S+)$/){ + if ($1 eq $type){ + print $1 . ".value " . $2 . "\n"; + } + } + } + } +} + +sub multi_config{ + my $id = $_[0]; + my %urls = %{$_[1]}; + my %cache = %{$_[2]}; + + my $count = 0; + + + print "multigraph http_load_$id\n"; + loadtime_config($id, \%urls, \%cache); + + print "\nmultigraph http_load_$id.loadtime\n"; + loadtime_config($id, \%urls, \%cache); + + print "\nmultigraph http_load_$id.size\n"; + size_config($id, \%urls, \%cache); + + print "\nmultigraph http_load_$id.elements\n"; + elements_config($id, \%urls, \%cache); + + print "\nmultigraph http_load_$id.response\n"; + response_config($id, \%urls, \%cache); + + print "\nmultigraph http_load_$id.type\n"; + type_config($id, \%urls, \%cache); + + print "\nmultigraph http_load_$id.tags\n"; + tags_config($id, \%urls, \%cache); + +} + +sub multi_values{ + my $id = $_[0]; + my %cache = %{$_[1]}; + + my $count = 0; + + + print "multigraph http_load_$id\n"; + cache_values(\%cache, "loadtime"); + + print "\nmultigraph http_load_$id.loadtime\n"; + cache_values(\%cache, "loadtime"); + + print "\nmultigraph http_load_$id.size\n"; + cache_values(\%cache, "size"); + + print "\nmultigraph http_load_$id.elements\n"; + cache_values(\%cache, "elements"); + + print "\nmultigraph http_load_$id.response\n"; + cache_values(\%cache, "response"); + + print "\nmultigraph http_load_$id.type\n"; + cache_values(\%cache, "type"); + + print "\nmultigraph http_load_$id.tags\n"; + cache_values(\%cache, "tags"); + +} $debug && print "Scriptname: " . $scriptname . "\n"; # Get the url id and the type of the graph @@ -198,9 +579,13 @@ $debug && print "Scriptname: " . $scriptname . "\n"; # Y: The type of graph (elements, size, loadtime, ..) my ($id,$type); -$0 =~ /http_load(?:_([^_]+)|)_(.+)\s*$/; +$0 =~ /http_load(?:_([^_]+)|)(_(.+))?\s*$/; $id = $1; -$type = $2; +$type = $3; + +if($type eq "") { + $type = "multi"; +} $debug && print "Id: $id, Type: $type\n"; @@ -219,12 +604,7 @@ if($ARGV[0] and $ARGV[0] eq "autoconf") { my %urls=&read_urls($url_file); while ( my ($id, $url) = each(%urls) ) { $debug && print "id: $id => url: $url\n"; - print $id . "_size\n"; - print $id . "_loadtime\n"; - print $id . "_response\n"; - print $id . "_tags\n"; - print $id . "_type\n"; - print $id . "_elements\n"; + print $id . "\n"; } exit(0); @@ -233,7 +613,10 @@ if($ARGV[0] and $ARGV[0] eq "autoconf") { # read from my $verbose=0; - if($ARGV[1] and $ARGV[1] eq "verbose") { + if( + $ENV{MUNIN_DEBUG} eq "1" or + $ARGV[1] and $ARGV[1] eq "verbose" + ) { $verbose=1; print "Verbose output\n"; } @@ -282,6 +665,8 @@ if($ARGV[0] and $ARGV[0] eq "autoconf") { $output{"response_" . $host . "_" . $response->code}+=1; $output{"type_" . $response->content_type}+=1; + # For s, also capture the rel attribute + $HTML::Tagset::linkElements{'link'} = [ qw( href rel ) ]; $page_parser = HTML::LinkExtor->new(undef, $url); $page_parser->parse($contents)->eof; my @links = $page_parser->links; @@ -289,15 +674,20 @@ if($ARGV[0] and $ARGV[0] eq "autoconf") { %res=(); foreach $link (@links){ - my $tag=$$link[0] . " " . $$link[1]; - + my $tag; + my($t, %attrs) = @{$link}; + if ($attrs{rel} =~ /.*\/([^\/]+)/) { + $tag=$$link[0] . " " . $1; + } else { + $tag=$$link[0] . " " . $$link[1]; + } $output{"tags_" . $$link[0] . "-" . $$link[1]}+=1; if(filter($tag)){ $verbose && print " Processing: " . $$link[0] . " " . $$link[1] . " " . $$link[2] . "\n"; # Extract the hostname and add it to the hash - if($$link[2] =~ m/http\:\/\/([^\/]+).*/){ + if($$link[2] =~ m/https?\:\/\/([^\/]+).*/){ $host=$1; $output{"elements_" . $host}+=1; } @@ -319,7 +709,7 @@ if($ARGV[0] and $ARGV[0] eq "autoconf") { } } - $cachefile=$cachedir . "/" . &get_cache_file_name($scriptname,$id,$type); + $cachefile=$cachedir . "/" . &get_cache_file_name($scriptname,$id); $debug && print "Reading cache file: " . $cachefile . "... "; my %input=read_cache($cachefile); @@ -350,222 +740,38 @@ if($ARGV[0] and $ARGV[0] eq "autoconf") { }elsif($ARGV[0] and $ARGV[0] eq "config") { my %urls=&read_urls($url_file); - print "graph_title $urls{$id} ${type}\n"; - print "graph_args -l 0 --base 1000\n"; - print "graph_category " . $category . "\n"; $debug && print "Reading cache file\n"; - my $cachefile=$cachedir . "/" . &get_cache_file_name($scriptname,$id,$type); + my $cachefile=$cachedir . "/" . &get_cache_file_name($scriptname,$id); my %cache=read_cache($cachefile); - my $count=0; $debug && print "The cache file contains " . keys(%cache) . " lines\n"; if($type eq "size"){ - print "graph_vlabel Bytes\n"; - print "graph_total Total\n"; - print "graph_info This graph is generated by a set of serial GETs to calculate the total size of $urls{$id}.\n"; - - if(keys(%cache)>0){ - for my $key ( sort reverse keys %cache ){ - my $value=$cache{$key}; - - if($key =~ m/^size_(\S+)$/){ - my $host=$1; - my $value=$value; - - my $name=$1; - $name=get_fieldname($name); - - print "$name.label from $host\n"; - print "$name.min 0\n"; - print "$name.max 20000000\n"; - if($count eq 0){ - print "$name.draw AREA\n"; - } else { - print "$name.draw STACK\n"; - } - $count+=1; - } - } - } + size_config($id, \%urls, \%cache) }elsif($type eq "loadtime"){ - print "graph_vlabel Seconds\n"; - print "graph_total Total\n"; - print "graph_info This graph is generated by a set of serial GETs to calculate the total time to load $urls{$id}. "; - print "Note that browsers usually fork() the GET requests, resulting in a shorter total loading time.\n"; - - if(keys(%cache)>0){ - for my $key ( sort reverse keys %cache ){ - my $value=$cache{$key}; - - if($key =~ m/^loadtime_(\S+)$/){ - my $host=$1; - my $value=$value; - - my $name=$1; - $name=get_fieldname($name); - - print "$name.label from $host\n"; - print "$name.min 0\n"; - print "$name.max 400\n"; - if($count eq 0){ - print "$name.draw AREA\n"; - } else { - print "$name.draw STACK\n"; - } - $count+=1; - } - } - } - + loadtime_config($id, \%urls, \%cache) }elsif($type eq "elements"){ - print "graph_vlabel Number of elements\n"; - print "graph_total Total\n"; - print "graph_info This graph is generated by a set of serial GETs to count the number of elements (images, CSS files, etc) from $urls{$id}.\n"; - - if(keys(%cache)>0){ - for my $key ( sort reverse keys %cache ){ - my $value=$cache{$key}; - - if($key =~ m/^elements_(\S+)$/){ - my $host=$1; - my $value=$value; - - my $name=$1; - $name=get_fieldname($name); - - print "$name.label from $host\n"; - print "$name.min 0\n"; - print "$name.max 10000\n"; - if($count eq 0){ - print "$name.draw AREA\n"; - } else { - print "$name.draw STACK\n"; - } - $count+=1; - } - } - } + elements_config($id, \%urls, \%cache) }elsif($type eq "response"){ - print "graph_vlabel Server response code count\n"; - print "graph_total Total\n"; - print "graph_info This graph is generated by a set of serial GETs to visualize the server response codes received while loading $urls{$id}.\n"; - - if(keys(%cache)>0){ - for my $key ( sort reverse keys %cache ){ - my $value=$cache{$key}; - - if($key =~ m/^response_(\S+)$/){ - my $host=$1; - my $value=$value; - - my $name=$1; - $name=get_fieldname($name); - - $host =~ s/\_/ /g; - $host =~ s/(\S+)\s(\d+)/ /g; - $host=$1; - my $code=$2; - - print "$name.label $host ($code)\n"; - print "$name.min 0\n"; - print "$name.max 10000\n"; - if($count eq 0){ - print "$name.draw AREA\n"; - } else { - print "$name.draw STACK\n"; - } - $count+=1; - } - } - } + response_config($id, \%urls, \%cache) }elsif($type eq "type"){ - print "graph_vlabel Content type count\n"; - print "graph_total Total\n"; - print "graph_info This graph is generated by a set of serial GETs to visualize the different content types $urls{$id} consists of.\n"; - - if(keys(%cache)>0){ - for my $key ( sort reverse keys %cache ){ - my $value=$cache{$key}; - - if($key =~ m/^type_(\S+)$/){ - my $type=$1; - my $value=$value; - - my $name=$1; - $name=get_fieldname($name); - - #$host =~ s/\_/ /g; - #$host =~ s/(\S+)\s(\S+)/ /g; - #$host=$1; - #my $type=$2; - - print "$name.label $type\n"; - print "$name.min 0\n"; - print "$name.max 100000\n"; - if($count eq 0){ - print "$name.draw AREA\n"; - } else { - print "$name.draw STACK\n"; - } - $count+=1; - } - } - } + type_config($id, \%urls, \%cache) }elsif($type eq "tags"){ - print "graph_vlabel HTML tag count\n"; - print "graph_total Total\n"; - print "graph_info This graph is generated by a set of serial GETs to visualize the different tags $urls{$id} consists of.\n"; - - if(keys(%cache)>0){ - for my $key ( sort reverse keys %cache ){ - my $value=$cache{$key}; - - if($key =~ m/^tags_(\S+)$/){ - my $host=$1; - my $value=$value; - - my $name=$1; - $name=get_fieldname($name); - - $host =~ s/\W/ /g; - - print "$name.label $host\n"; - print "$name.min 0\n"; - print "$name.max 100000\n"; - if($count eq 0){ - print "$name.draw AREA\n"; - } else { - print "$name.draw STACK\n"; - } - $count+=1; - } - } - } + tags_config($id, \%urls, \%cache) + }elsif($type eq "multi"){ + multi_config($id, \%urls, \%cache) } - exit(0); + exit(0); } else { - my $cachefile=$cachedir . "/" . &get_cache_file_name($scriptname,$id,$type); + my $cachefile=$cachedir . "/" . &get_cache_file_name($scriptname,$id); $debug && print "Reading cache file: " . $cachefile . "\n"; my %cache=read_cache($cachefile); $debug && print "Number of lines in cache file: " . keys(%cache) . "\n"; - if(keys(%cache)>0){ - for my $key ( sort keys %cache ){ - my $value=$cache{$key}; - if($key =~ m/^([A-Za-z]+)\_(\S+)$/){ - my $name=$2; - - if ($1 eq $type){ - $name=get_fieldname($name); - print $name . ".value " . $value . "\n"; - } - } elsif(m/^(\S+)\s+(\S+)$/){ - if ($1 eq $type){ - print $1 . ".value " . $2 . "\n"; - } - } - } + if($type eq "multi"){ + multi_values($id, \%cache); + } else { + cache_values(\%cache, $type); } } diff --git a/plugins/http/http_request_time b/plugins/http/http_request_time index 1732da67..c8c4b9a0 100755 --- a/plugins/http/http_request_time +++ b/plugins/http/http_request_time @@ -110,7 +110,7 @@ if ( defined $ARGV[0] and $ARGV[0] eq "config" ) print "graph_title HTTP(S) Request response times\n"; print "graph_args --base 1000\n"; print "graph_vlabel response time in ms\n"; - print "graph_category other\n"; + print "graph_category webserver\n"; my @go; foreach my $name (keys %URLS) { @@ -130,7 +130,7 @@ if ( defined $ARGV[0] and $ARGV[0] eq "config" ) print "graph_title $$url{'url'}\n"; print "graph_args --base 1000\n"; print "graph_vlabel response time in ms\n"; - print "graph_category other\n"; + print "graph_category webserver\n"; print "$name.label $$url{'label'}\n"; print "$name.info The response time of a single request\n"; print "$name.min 0\n"; diff --git a/plugins/http/http_responsetime b/plugins/http/http_responsetime index 08e2f677..a36e8450 100755 --- a/plugins/http/http_responsetime +++ b/plugins/http/http_responsetime @@ -64,7 +64,7 @@ if ($ARGV[0] && $ARGV[0] eq "autoconf") { print "graph_title HTTP response time\n"; } print "graph_vlabel ms\n"; - print "graph_category HTTP\n"; + print "graph_category webserver\n"; print "graph_info This graph shows the response time in milliseconds, to load a web page\n"; print "timespent.label timespent\n"; print "timespent.type GAUGE\n"; diff --git a/plugins/http/multi_http_responsetime b/plugins/http/multi_http_responsetime index c5b0664a..94086e05 100755 --- a/plugins/http/multi_http_responsetime +++ b/plugins/http/multi_http_responsetime @@ -92,7 +92,7 @@ if ($ARGV[0] && $ARGV[0] eq "autoconf") { } print "graph_scale no\n"; print "graph_vlabel ms\n"; - print "graph_category HTTP\n"; + print "graph_category webserver\n"; print "graph_info This graph shows the response time in milliseconds, to load a web page\n"; for ($i=0; $i <= $max_index; ++$i) { $vhost = $url_array[$i]; diff --git a/plugins/http/vhost_requests_ b/plugins/http/vhost_requests_ index 1682bd98..328b4885 100755 --- a/plugins/http/vhost_requests_ +++ b/plugins/http/vhost_requests_ @@ -64,7 +64,7 @@ file_name=`basename $0 | sed 's/^vhost_requests_//g'` case $1 in config) -echo graph_category http +echo graph_category webserver echo graph_title Requests by Status Code $file_name echo graph_vlabel Nr of Requests echo S200.label 200 OK diff --git a/plugins/http/wget_page b/plugins/http/wget_page index 467b46f2..00081281 100755 --- a/plugins/http/wget_page +++ b/plugins/http/wget_page @@ -143,7 +143,7 @@ if [ "${1}" = "config" ]; then echo "graph_args --base 1000 -l 0" echo "graph_scale no" echo "graph_vlabel Load time in seconds" - echo "graph_category http" + echo "graph_category webserver" echo "graph_info This graph shows load time in seconds of one or more urls" I=1 for name in ${names}; do diff --git a/plugins/other/i2p_ b/plugins/i2p/i2p_ similarity index 97% rename from plugins/other/i2p_ rename to plugins/i2p/i2p_ index f066d8a0..1c4d93bf 100755 --- a/plugins/other/i2p_ +++ b/plugins/i2p/i2p_ @@ -26,7 +26,7 @@ def config(): print('graph_title i2p bps') print('graph_vlabel bps') print('graph_info i2p sending and receiving bytes per second') - print('graph_category i2p') + print('graph_category network') print('receivebps.label Receive bps') print('sendbps.label Send bps') elif 'uptime' == plugin_var: @@ -34,7 +34,7 @@ def config(): print('graph_scale no') print('graph_args --base 1000 -l 0') print('graph_vlabel uptime in whole hours') - print('graph_category i2p') + print('graph_category network') print('uptime.label i2p uptime') print('uptime.draw AREA') else: diff --git a/plugins/streaming/icecast2 b/plugins/icecast/icecast2 similarity index 100% rename from plugins/streaming/icecast2 rename to plugins/icecast/icecast2 diff --git a/plugins/streaming/icecast2_ b/plugins/icecast/icecast2_ old mode 100644 new mode 100755 similarity index 100% rename from plugins/streaming/icecast2_ rename to plugins/icecast/icecast2_ diff --git a/plugins/streaming/icecast2_all b/plugins/icecast/icecast2_all old mode 100644 new mode 100755 similarity index 100% rename from plugins/streaming/icecast2_all rename to plugins/icecast/icecast2_all diff --git a/plugins/streaming/icecast2_simple b/plugins/icecast/icecast2_simple similarity index 100% rename from plugins/streaming/icecast2_simple rename to plugins/icecast/icecast2_simple diff --git a/plugins/streaming/icecast2_stats_ b/plugins/icecast/icecast2_stats_ similarity index 97% rename from plugins/streaming/icecast2_stats_ rename to plugins/icecast/icecast2_stats_ index ea6a159c..744fe10c 100755 --- a/plugins/streaming/icecast2_stats_ +++ b/plugins/icecast/icecast2_stats_ @@ -151,20 +151,20 @@ if __name__ == "__main__": if scope == "sources_listeners": print("graph_title Total number of listeners") print("graph_vlabel listeners") - print("graph_category Icecast") + print("graph_category streaming") for index, source in enumerate(get_sources()): print("{0}.label {1}".format(source["fieldname"], source["name"])) print("{0}.draw {1}".format(source["fieldname"], ("AREA" if (index == 0) else "STACK"))) elif scope == "sources_duration": print("graph_title Duration of sources") print("graph_vlabel duration in days") - print("graph_category Icecast") + print("graph_category streaming") for source in get_sources(): print("{0}.label {1}".format(source["fieldname"], source["name"])) elif scope == "service_uptime": print("graph_title Icecast service uptime") print("graph_vlabel uptime in days") - print("graph_category Icecast") + print("graph_category streaming") print("uptime.label service uptime") elif action is None: scope = get_scope() diff --git a/plugins/streaming/icecast_ b/plugins/icecast/icecast_ similarity index 100% rename from plugins/streaming/icecast_ rename to plugins/icecast/icecast_ diff --git a/plugins/mail/imapproxy_multi b/plugins/imapproxy/imapproxy_multi similarity index 97% rename from plugins/mail/imapproxy_multi rename to plugins/imapproxy/imapproxy_multi index e056f6b0..12afc642 100755 --- a/plugins/mail/imapproxy_multi +++ b/plugins/imapproxy/imapproxy_multi @@ -46,7 +46,7 @@ def print_config(): print "graph_args -l 0 --base 1000" print "graph_total total" print "graph_vlabel Cache Connections / ${graph_period}" - print "graph_category imapproxy" + print "graph_category mail" print "cache_hits.draw AREA" print "cache_hits.type DERIVE" print "cache_hits.label Cache Hits" @@ -61,7 +61,7 @@ def print_config(): print "graph_args -l 0 --base 1000" print "graph_total total" print "graph_vlabel Connections / ${graph_period}" - print "graph_category imapproxy" + print "graph_category mail" print "connections_reused.draw AREA" print "connections_reused.type DERIVE" print "connections_reused.label Reused Connections" diff --git a/plugins/network/ip6_ b/plugins/ip6/ip6_ similarity index 79% rename from plugins/network/ip6_ rename to plugins/ip6/ip6_ index 099de58c..20408378 100755 --- a/plugins/network/ip6_ +++ b/plugins/ip6/ip6_ @@ -28,7 +28,7 @@ IP=`basename $0 | sed 's/^ip6_//g' | tr '_' ':' ` if [ "$1" = "autoconf" ]; then if [ -r /proc/net/dev ]; then - ip6tables -L INPUT -v -n -x >/dev/null 2>/dev/null + ip6tables -L INPUT -v -n -x -w >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "no (could not run ip6tables as user `whoami`)" exit 1 @@ -43,7 +43,7 @@ if [ "$1" = "autoconf" ]; then fi if [ "$1" = "suggest" ]; then - ip6tables -L INPUT -v -n -x 2>/dev/null | awk --posix '$8 ~ /^([0-9a-f]{1,4}(\:|\:\:)){1,7}([0-9a-f]{1,4})\/([0-9]{1,3})$/ { if (done[$8]!=1) {print $8; done[$8]=1;}}'|sed "s#/[0-9]\{1,3\}##" + ip6tables -L INPUT -v -n -x -w 2>/dev/null | awk --posix '$8 ~ /^([0-9a-f]{1,4}(\:|\:\:)){1,7}([0-9a-f]{1,4})\/([0-9]{1,3})$/ { if (done[$8]!=1) {print $8; done[$8]=1;}}'|sed "s#/[0-9]\{1,3\}##" exit 0 fi @@ -64,5 +64,5 @@ if [ "$1" = "config" ]; then exit 0 fi; -ip6tables -L INPUT -v -n -x | grep -m1 $IP | awk "{ print \"in.value \" \$2 }" -ip6tables -L OUTPUT -v -n -x | grep -m1 $IP | awk "{ print \"out.value \" \$2 }" +ip6tables -L INPUT -v -n -x -w | grep -m1 $IP | awk "{ print \"in.value \" \$2 }" +ip6tables -L OUTPUT -v -n -x -w | grep -m1 $IP | awk "{ print \"out.value \" \$2 }" diff --git a/plugins/network/ip6t_accounting b/plugins/ip6/ip6t_accounting similarity index 50% rename from plugins/network/ip6t_accounting rename to plugins/ip6/ip6t_accounting index 2919a9c2..aaaebe50 100755 --- a/plugins/network/ip6t_accounting +++ b/plugins/ip6/ip6t_accounting @@ -10,7 +10,7 @@ graph_category network graph_args -l 0 graph_info ip6tables bites ipv6 EOF -ip6tables -vxL|grep -E 'ACC|REJ'|grep -v ^Chain|sed -e 's/dpt://g' -e 's/ .*://g'|awk '{print $NF"-"$4".label", $NF" "$4"\n",$NF"-"$4".min", 0}'|sed 's/^\s*//g' +ip6tables -vx -w -L | grep -E 'ACC|REJ'|grep -v ^Chain|sed -e 's/dpt://g' -e 's/ .*://g'|awk '{print $NF"-"$4".label", $NF" "$4"\n",$NF"-"$4".min", 0}'|sed 's/^\s*//g' exit 0 fi if [ "$1" = "autoconf" ]; then @@ -18,5 +18,5 @@ if [ "$1" = "autoconf" ]; then exit 0 fi -ip6tables -vxL|grep -E 'ACC|REJ'|grep -v ^Chain|awk '{print $NF"-"$4".value", $2}'|sed 's/^dpt://' +ip6tables -vx -w -L | grep -E 'ACC|REJ'|grep -v ^Chain|awk '{print $NF"-"$4".value", $2}'|sed 's/^dpt://' diff --git a/plugins/sensors/freeipmi b/plugins/ipmi/freeipmi similarity index 100% rename from plugins/sensors/freeipmi rename to plugins/ipmi/freeipmi diff --git a/plugins/ipvs/ipvs_active b/plugins/ipvs/ipvs_active index b9e37973..c6fd9255 100755 --- a/plugins/ipvs/ipvs_active +++ b/plugins/ipvs/ipvs_active @@ -45,7 +45,7 @@ if [ "$1" = "config" ]; then echo 'graph_args --base 1000 -l 0 ' echo 'graph_vlabel Servers' echo 'graph_scale no' - echo 'graph_category Ipvs' + echo 'graph_category loadbalancer' echo 'graph_info Indicate the number of active servers in Ipvs.' for IP in $IPLIST; do diff --git a/plugins/ipvs/ipvs_bps b/plugins/ipvs/ipvs_bps index df198ce3..70e045e3 100755 --- a/plugins/ipvs/ipvs_bps +++ b/plugins/ipvs/ipvs_bps @@ -44,7 +44,7 @@ if [ "$1" = "config" ]; then echo 'graph_title Ipvs Traffic' echo 'graph_args --base 1000 -l 0 ' echo 'graph_vlabel Bytes in (-) / out (+)' - echo 'graph_category Ipvs' + echo 'graph_category loadbalancer' echo 'graph_info Indicate the ipvs traffic.' for IP in $IPLIST; do diff --git a/plugins/ipvs/ipvs_conn b/plugins/ipvs/ipvs_conn index ce5dc922..fb480b92 100755 --- a/plugins/ipvs/ipvs_conn +++ b/plugins/ipvs/ipvs_conn @@ -45,7 +45,7 @@ if [ "$1" = "config" ]; then echo 'graph_args --base 1000 -l 0 ' echo 'graph_vlabel Connections' echo 'graph_scale no' - echo 'graph_category Ipvs' + echo 'graph_category loadbalancer' echo 'graph_info Indicate the number of active and inactive connections in Ipvs.' for IP in $IPLIST; do diff --git a/plugins/ipvs/ipvs_cps b/plugins/ipvs/ipvs_cps index 10081cee..41654535 100755 --- a/plugins/ipvs/ipvs_cps +++ b/plugins/ipvs/ipvs_cps @@ -5,7 +5,7 @@ =head1 NAME -ipvs_conn -Indicate the number of conections per second +ipvs_conn -Indicate the number of connections per second =head1 CONFIGURATION @@ -45,7 +45,7 @@ if [ "$1" = "config" ]; then echo 'graph_args --base 1000 -l 0 ' echo 'graph_vlabel Connections' echo 'graph_scale no' - echo 'graph_category Ipvs' + echo 'graph_category loadbalancer' echo 'graph_info Indicate the number of connections per second.' for IP in $IPLIST; do diff --git a/plugins/other/irc2 b/plugins/ircd/irc2 similarity index 99% rename from plugins/other/irc2 rename to plugins/ircd/irc2 index 7c90f54a..4a37c396 100755 --- a/plugins/other/irc2 +++ b/plugins/ircd/irc2 @@ -39,6 +39,7 @@ my $server = $ENV{SERVER} || 'localhost'; if($ARGV[0] and $ARGV[0] eq "config") { print "host_name $server\n"; print "graph_title ircd status - $server\n"; + print "graph_category chat\n"; print "graph_order clients channels servers localclients clientmax localclientmax localservers opers unknownconns\n"; print "graph_args -l 0\n"; print "clients.label clients\n"; diff --git a/plugins/services/ircd b/plugins/ircd/ircd similarity index 98% rename from plugins/services/ircd rename to plugins/ircd/ircd index 06fa8afd..7770d354 100755 --- a/plugins/services/ircd +++ b/plugins/ircd/ircd @@ -23,7 +23,7 @@ if (isset($argv['1']) && $argv['1'] == "config") { print "host_name ".php_uname('n')."\n"; print "graph_title IRCd Status\n"; - print "graph_category Services\n"; + print "graph_category chat\n"; print "graph_order clients channels operators\n"; print "graph_args --base 1000 -l 0\n"; diff --git a/plugins/jchkmail_counters_/jchkmail_counters_ b/plugins/jchkmail/jchkmail_counters_ old mode 100644 new mode 100755 similarity index 97% rename from plugins/jchkmail_counters_/jchkmail_counters_ rename to plugins/jchkmail/jchkmail_counters_ index 3302bed4..ef30fa96 --- a/plugins/jchkmail_counters_/jchkmail_counters_ +++ b/plugins/jchkmail/jchkmail_counters_ @@ -575,7 +575,7 @@ ratiospamham01.info Unwanted messages == BEGIN config == activity-graph_title j-chkmail - Filter activity -activity-graph_category j-chkmail +activity-graph_category mail activity-graph_vlabel counts per second activity-graph_scale no activity+data conn msgs rcpt @@ -583,7 +583,7 @@ activity+type counters activity+enable yes greylisting-graph_title j-chkmail - Greylisting activity -greylisting-graph_category j-chkmail +greylisting-graph_category mail greylisting-graph_vlabel counts per second greylisting-graph_scale no greylisting+data greymsgs greyrcpt @@ -591,7 +591,7 @@ greylisting+type counters greylisting+enable yes statfilter-graph_title j-chkmail - Statistical classification -statfilter-graph_category j-chkmail +statfilter-graph_category mail statfilter-graph_vlabel counts per second statfilter-graph_scale no statfilter+data bayesham bayesspam @@ -599,7 +599,7 @@ statfilter+type counters statfilter+enable yes contentfilter-graph_title j-chkmail - Content Filtering -contentfilter-graph_category j-chkmail +contentfilter-graph_category mail contentfilter-graph_vlabel counts per second contentfilter-graph_scale no contentfilter+data bayesspam matching urlbl @@ -607,7 +607,7 @@ contentfilter+type counters contentfilter+enable yes behaviour-graph_title j-chkmail - Behaviour filtering - rejections -behaviour-graph_category j-chkmail +behaviour-graph_category mail behaviour-graph_vlabel counts per second behaviour-graph_scale no behaviour+data throttle badrcpt localuser badmx rcptrate @@ -615,7 +615,7 @@ behaviour+type counters behaviour+enable yes xfiles-graph_title j-chkmail - Suspected files filtering -xfiles-graph_category j-chkmail +xfiles-graph_category mail xfiles-graph_vlabel counts per second xfiles-graph_scale no xfiles+data files xfiles @@ -623,7 +623,7 @@ xfiles+type counters xfiles+enable yes volume-graph_title j-chkmail - Volume handled by the filter -volume-graph_category j-chkmail +volume-graph_category mail volume-graph_vlabel KBytes per second volume-graph_scale no volume+data kbytes @@ -631,7 +631,7 @@ volume+type counters volume+enable yes ratiospamham-graph_title j-chkmail - Statistical classification -ratiospamham-graph_category j-chkmail +ratiospamham-graph_category mail ratiospamham-graph_vlabel Ratio (%) ratiospamham-graph_scale yes ratiospamham-graph_args --lower-limit 0 --upper-limit 100 --rigid @@ -641,7 +641,7 @@ ratiospamham+state yes ratiospamham+enable yes greydb-graph_title j-chkmail - Greylisting Database Size -greydb-graph_category j-chkmail +greydb-graph_category mail greydb-graph_vlabel records greydb-graph_scale no greydb-graph_args --lower-limit 0 @@ -650,7 +650,7 @@ greydb+type dbcounters greydb+enable yes cdb-graph_title j-chkmail - Static databases -cdb-graph_category j-chkmail +cdb-graph_category mail cdb-graph_vlabel records cdb-graph_scale no cdb-graph_args --lower-limit 0 diff --git a/plugins/jenkins/jenkins_ b/plugins/jenkins/jenkins_ old mode 100644 new mode 100755 index 7dd34068..d3d90c70 --- a/plugins/jenkins/jenkins_ +++ b/plugins/jenkins/jenkins_ @@ -54,6 +54,7 @@ use warnings; use strict; use JSON; use File::Basename; +use URI; # VARS my $url = ($ENV{'url'} || 'localhost'); @@ -79,14 +80,14 @@ my %states = ( 'aborted'=>'failing', 'aborted_anime'=>'failing' ); -my %counts = ('stable' => 0, 'unstable'=>0, 'failing'=>0, 'disabled'=>0); +my $auth = ( $user ne "" and $apiToken ne "" ? " --auth-no-challenge --user=$user --password=$apiToken" : "" ); if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { if( $type eq "results" ) { print "graph_args --base 1000 -l 0\n"; print "graph_title Jenkins Build Results\n"; print "graph_vlabel Build Results\n"; - print "graph_category Jenkins\n"; + print "graph_category devel\n"; print "graph_info The Graph shows the Status of each Build\n"; print "build_disabled.draw AREA\n"; print "build_disabled.label disabled\n"; @@ -103,14 +104,14 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { print "build_stable.draw STACK\n"; print "build_stable.label stable\n"; print "build_stable.type GAUGE\n"; - print "build_stable.colour 294D99\n"; + print "build_stable.colour 294D99\n"; exit; } if( $type eq "queue" ) { print "graph_args --base 1000 -l 0\n"; print "graph_title Jenkins Queue Length\n"; print "graph_vlabel Number of Jobs in Queue\n"; - print "graph_category Jenkins\n"; + print "graph_category devel\n"; print "graph_info The Graph shows the Number of Jobs in the Build Queue\n"; print "build_count.label Jobs in Queue\n"; print "build_count.type GAUGE\n"; @@ -120,51 +121,85 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { print "graph_args --base 1000 -l 0\n"; print "graph_title Jenkins Builds Running\n"; print "graph_vlabel Builds currently running\n"; - print "graph_category Jenkins\n"; + print "graph_category devel\n"; print "graph_info The Graph shows the Number of Builds, currently running\n"; print "build_running.label running Builds\n"; print "build_running.type GAUGE\n"; exit; - } + } } else { - # CODE - my $auth = ( $user ne "" and $apiToken ne "" ? " --auth-no-challenge --user=$user --password=$apiToken" : "" ); - my $cmd = "$wgetBin $auth -qO- $url:$port$context"; - + my $cmd = "$wgetBin $auth -qO- $url:$port$context"; + if( $type eq "results" ) { - my $result = `$cmd/api/json`; - my $parsed = decode_json($result); - foreach my $cur(@{$parsed->{'jobs'}}) { - if (defined $states{$cur->{'color'}}) { - $counts{$states{$cur->{'color'}}} += 1; - } else { - warn "Ignoring unknown color " . $cur->{'color'} . "\n" - } - } - - foreach my $status (keys %counts) { - print "build_$status.value $counts{$status}\n"; + my $counts = get_results(''); + + foreach my $status (keys %{$counts}) { + print "build_$status.value $counts->{$status}\n"; } exit; } - + if( $type eq "running" ) { - my $count = 0; - my $result = `$cmd/api/json`; - my $parsed = decode_json($result); - foreach my $cur(@{$parsed->{'jobs'}}) { - if( $cur->{'color'} =~ /anime$/ ) { - $count += 1; - } - } - print "build_running.value ", $count, "\n"; - exit; + my $running_count = get_running(''); + print "build_running.value ", $running_count, "\n"; + exit; } - + if( $type eq "queue" ) { my $result = `$cmd/queue/api/json`; - my $parsed = decode_json($result); + my $parsed = decode_json($result); print "build_count.value ", scalar( @{$parsed->{'items'}} ), "\n"; exit; } } + +{ + my %counts; + + sub get_results{ + my $query_context = shift; + die "get_results requires an argument" unless defined $query_context; + unless (%counts) { + # initialise + %counts = ('stable' => 0, 'unstable'=>0, 'failing'=>0, 'disabled'=>0); + } + my $cmd = "$wgetBin $auth -qO- $url:$port$context$query_context/api/json"; + my $result = `$cmd`; + my $parsed = decode_json($result); + foreach my $cur(@{$parsed->{'jobs'}}) { + if (exists $cur->{'color'} && defined $states{$cur->{'color'}}) { + $counts{$states{$cur->{'color'}}} += 1; + } elsif ($cur->{'_class'} eq 'com.cloudbees.hudson.plugins.folder.Folder'){ + my $uri = URI->new($cur->{'url'}); + my $folder = $uri->path; + get_results($folder); + } else { + warn "Ignoring unknown color " . $cur->{'color'} . "\n" + } + } + return \%counts; + } +} + +{ + my $running_count; + sub get_running{ + my $query_context = shift; + die "get_results requires an argument" unless defined $query_context; + $running_count //= 0; + my $cmd = "$wgetBin $auth -qO- $url:$port$context$query_context/api/json"; + my $result = `$cmd`; + my $parsed = decode_json($result); + foreach my $cur(@{$parsed->{'jobs'}}) { + if( exists $cur->{'color'} && $cur->{'color'} =~ /anime$/ ) { + $running_count += 1; + } elsif ($cur->{'_class'} eq 'com.cloudbees.hudson.plugins.folder.Folder'){ + my $uri = URI->new($cur->{'url'}); + my $folder = $uri->path; + get_running($folder); + } + } + return $running_count; + } +} + diff --git a/plugins/jenkins/jenkins_nodes_ b/plugins/jenkins/jenkins_nodes_ old mode 100644 new mode 100755 index 20ce41fa..595e7f96 --- a/plugins/jenkins/jenkins_nodes_ +++ b/plugins/jenkins/jenkins_nodes_ @@ -25,7 +25,7 @@ my $cmd = "$wgetBin $auth -qO- $url:$port$context"; my $monitor; if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { - my $base_config = "graph_category Jenkins\n"; + my $base_config = "graph_category devel\n"; if( $type eq "mem_sum" ) { print $base_config; @@ -51,7 +51,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { my $result = `$cmd/computer/api/json`; my $parsed = decode_json($result); foreach my $cur(@{$parsed->{'computer'}}) { - if( $cur->{'offline'} =~ /false$/ ) { + if( !$cur->{'offline'} ) { my $cat = $cur->{'displayName'}; $cat =~ s/\./\_/g; if( $lcount > 0 ){ @@ -77,7 +77,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { my $result = `$cmd/computer/api/json`; my $parsed = decode_json($result); foreach my $cur(@{$parsed->{'computer'}}) { - if( $cur->{'offline'} =~ /false$/ ) { + if( !$cur->{'offline'} ) { my $cat = $cur->{'displayName'}; $cat =~ s/\./\_/g; if( $lcount > 0 ){ @@ -103,7 +103,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { my $result = `$cmd/computer/api/json`; my $parsed = decode_json($result); foreach my $cur(@{$parsed->{'computer'}}) { - if( $cur->{'offline'} =~ /false$/ ) { + if( !$cur->{'offline'} ) { my $cat = $cur->{'displayName'}; $cat =~ s/\./\_/g; if( $lcount > 0 ){ @@ -130,7 +130,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { my %archs = (); my $cat; foreach my $cur(@{$parsed->{'computer'}}) { - if( $cur->{'offline'} =~ /false$/ ) { + if( !$cur->{'offline'} ) { $cat = $cur->{'monitorData'}{'hudson.node_monitors.ArchitectureMonitor'}; if (exists $archs{$cat} ) {} else { $archs{$cat} = 0; @@ -178,7 +178,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { my $total_mem = 0; my $used_mem = 0; foreach my $cur(@{$parsed->{'computer'}}) { - if( $cur->{'offline'} =~ /false$/ ) { + if( !$cur->{'offline'} ) { $monitor = $cur->{'monitorData'}{'hudson.node_monitors.SwapSpaceMonitor'}; $avail_mem += $monitor->{'availablePhysicalMemory'}; $total_mem += $monitor->{'totalPhysicalMemory'}; @@ -193,7 +193,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { my $result = `$cmd/computer/api/json`; my $parsed = decode_json($result); foreach my $cur(@{$parsed->{'computer'}}) { - if( $cur->{'offline'} =~ /false$/ ) { + if( !$cur->{'offline'} ) { $monitor = $cur->{'monitorData'}{'hudson.node_monitors.SwapSpaceMonitor'}; my $cat = $cur->{'displayName'}; $cat =~ s/\./\_/g; @@ -206,7 +206,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { my $result = `$cmd/computer/api/json`; my $parsed = decode_json($result); foreach my $cur(@{$parsed->{'computer'}}) { - if( $cur->{'offline'} =~ /false$/ ) { + if( !$cur->{'offline'} ) { $monitor = $cur->{'monitorData'}{'hudson.node_monitors.TemporarySpaceMonitor'}; my $cat = $cur->{'displayName'}; $cat =~ s/\./\_/g; @@ -219,7 +219,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { my $result = `$cmd/computer/api/json`; my $parsed = decode_json($result); foreach my $cur(@{$parsed->{'computer'}}) { - if( $cur->{'offline'} =~ /false$/ ) { + if( !$cur->{'offline'} ) { $monitor = $cur->{'monitorData'}{'hudson.node_monitors.DiskSpaceMonitor'}; my $cat = $cur->{'displayName'}; $cat =~ s/\./\_/g; @@ -233,7 +233,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { my %archs = (); my $cat; foreach my $cur(@{$parsed->{'computer'}}) { - if( $cur->{'offline'} =~ /false$/ ) { + if( !$cur->{'offline'} ) { $cat = $cur->{'monitorData'}{'hudson.node_monitors.ArchitectureMonitor'}; if (exists $archs{$cat} ) { $archs{$cat} += 1; @@ -271,4 +271,4 @@ sub clean_name { $name =~ s/\)/\_/g; $name =~ s/ //g; return $name; -} \ No newline at end of file +} diff --git a/plugins/java/jmx/examples/java/java_cpu.conf b/plugins/jmx/examples/java/java_cpu.conf similarity index 100% rename from plugins/java/jmx/examples/java/java_cpu.conf rename to plugins/jmx/examples/java/java_cpu.conf diff --git a/plugins/java/jmx/examples/java/java_process_memory.conf b/plugins/jmx/examples/java/java_process_memory.conf similarity index 100% rename from plugins/java/jmx/examples/java/java_process_memory.conf rename to plugins/jmx/examples/java/java_process_memory.conf diff --git a/plugins/java/jmx/examples/java/java_threads.conf b/plugins/jmx/examples/java/java_threads.conf similarity index 100% rename from plugins/java/jmx/examples/java/java_threads.conf rename to plugins/jmx/examples/java/java_threads.conf diff --git a/plugins/java/jmx/examples/tomcat/catalina_requests.conf b/plugins/jmx/examples/tomcat/catalina_requests.conf similarity index 100% rename from plugins/java/jmx/examples/tomcat/catalina_requests.conf rename to plugins/jmx/examples/tomcat/catalina_requests.conf diff --git a/plugins/java/jmx/examples/tomcat/catalina_threads.conf b/plugins/jmx/examples/tomcat/catalina_threads.conf similarity index 100% rename from plugins/java/jmx/examples/tomcat/catalina_threads.conf rename to plugins/jmx/examples/tomcat/catalina_threads.conf diff --git a/plugins/java/jmx/examples/tomcat/catalina_times.conf b/plugins/jmx/examples/tomcat/catalina_times.conf similarity index 100% rename from plugins/java/jmx/examples/tomcat/catalina_times.conf rename to plugins/jmx/examples/tomcat/catalina_times.conf diff --git a/plugins/java/jmx/examples/tomcat/catalina_traffic.conf b/plugins/jmx/examples/tomcat/catalina_traffic.conf similarity index 100% rename from plugins/java/jmx/examples/tomcat/catalina_traffic.conf rename to plugins/jmx/examples/tomcat/catalina_traffic.conf diff --git a/plugins/java/jmx/plugin/jmx_ b/plugins/jmx/plugin/jmx_ similarity index 100% rename from plugins/java/jmx/plugin/jmx_ rename to plugins/jmx/plugin/jmx_ diff --git a/plugins/java/jmx/plugin/jmxquery.jar b/plugins/jmx/plugin/jmxquery.jar similarity index 100% rename from plugins/java/jmx/plugin/jmxquery.jar rename to plugins/jmx/plugin/jmxquery.jar diff --git a/plugins/java/jmx/readme.txt b/plugins/jmx/readme.txt similarity index 100% rename from plugins/java/jmx/readme.txt rename to plugins/jmx/readme.txt diff --git a/plugins/joomla/joomla-sessions b/plugins/joomla/joomla-sessions index f98c7354..e93f38dd 100755 --- a/plugins/joomla/joomla-sessions +++ b/plugins/joomla/joomla-sessions @@ -69,7 +69,7 @@ if(defined $ARGV[0] && $ARGV[0] eq 'config') { print <cleanname, $x->name; diff --git a/plugins/lighttpd/lighttpd_ b/plugins/lighttpd/lighttpd_ index e31ff7f3..838bfa13 100755 --- a/plugins/lighttpd/lighttpd_ +++ b/plugins/lighttpd/lighttpd_ @@ -71,7 +71,7 @@ elif len(sys.argv) == 2 and sys.argv[1] == "config": params = graph_types[graph_type] for item in params: print "graph_title %s" % item["title"] - print "graph_category lighttpd" + print "graph_category webserver" for field in item["fields"]: print "%s.label %s" % (field, field) print "%s.type %s" % (field, item["type"]) diff --git a/plugins/system/load_extended b/plugins/load/load_extended similarity index 100% rename from plugins/system/load_extended rename to plugins/load/load_extended diff --git a/plugins/lustre/lustre_df b/plugins/lustre/lustre_df index 6836571b..86913419 100755 --- a/plugins/lustre/lustre_df +++ b/plugins/lustre/lustre_df @@ -49,7 +49,7 @@ if ($ARGV[0] and $ARGV[0] eq "config") { print "graph_args --base 1000\n"; print "graph_vlabel % usage\n"; print "graph_scale no\n"; - print "graph_category lustre\n"; + print "graph_category fs\n"; &print_labels; diff --git a/plugins/lustre/lustre_df_abs b/plugins/lustre/lustre_df_abs index a9395944..fb9bc400 100755 --- a/plugins/lustre/lustre_df_abs +++ b/plugins/lustre/lustre_df_abs @@ -49,7 +49,7 @@ if ($ARGV[0] and $ARGV[0] eq "config") { print "graph_title Lustre cluster storage objects usage in TB\n"; print "graph_args --base 1024 --lower-limit 0\n"; print "graph_vlabel TB\n"; - print "graph_category lustre\n"; + print "graph_category fs\n"; &print_labels; diff --git a/plugins/lustre/lustre_df_absfree b/plugins/lustre/lustre_df_absfree index 0160930e..3681cb14 100755 --- a/plugins/lustre/lustre_df_absfree +++ b/plugins/lustre/lustre_df_absfree @@ -49,7 +49,7 @@ if ($ARGV[0] and $ARGV[0] eq "config") { print "graph_title Lustre cluster storage objects free space in TB\n"; print "graph_args --base 1024 --lower-limit 0\n"; print "graph_vlabel TB\n"; - print "graph_category lustre\n"; + print "graph_category fs\n"; &print_labels; diff --git a/plugins/lustre/lustre_df_inodes b/plugins/lustre/lustre_df_inodes index e332060c..35626187 100755 --- a/plugins/lustre/lustre_df_inodes +++ b/plugins/lustre/lustre_df_inodes @@ -49,7 +49,7 @@ if ($ARGV[0] and $ARGV[0] eq "config") { print "graph_args --base 1000\n"; print "graph_vlabel % usage\n"; print "graph_scale no\n"; - print "graph_category lustre\n"; + print "graph_category fs\n"; &print_labels; diff --git a/plugins/lxc/lxc_cpu b/plugins/lxc/lxc_cpu index d8cbe9e1..87947c74 100755 --- a/plugins/lxc/lxc_cpu +++ b/plugins/lxc/lxc_cpu @@ -61,7 +61,7 @@ if [ "$1" = "config" ]; then echo 'graph_title CPU Usage ' echo 'graph_args -l 0 --base 1000' echo 'graph_vlabel USER_HZ' - echo 'graph_category lxc' + echo 'graph_category cpu' for guest_name in $guest_names; diff --git a/plugins/lxc/lxc_cpu_time b/plugins/lxc/lxc_cpu_time index 8a1d048c..22a5e1f8 100755 --- a/plugins/lxc/lxc_cpu_time +++ b/plugins/lxc/lxc_cpu_time @@ -60,7 +60,7 @@ if [ "$1" = "config" ]; then echo 'graph_title CPU time ' echo 'graph_args -l 0 --base 1000' echo 'graph_vlabel nanosec' - echo 'graph_category lxc' + echo 'graph_category cpu' for guest_name in $guest_names; do diff --git a/plugins/lxc/lxc_net b/plugins/lxc/lxc_net index 08759087..872147a7 100755 --- a/plugins/lxc/lxc_net +++ b/plugins/lxc/lxc_net @@ -66,14 +66,16 @@ for guest in `ls $lxcpath`; do continue; fi if [ -f "$lxcpath/$guest/config" ]; then - device=`grep '^lxc\.network\.veth\.pair[ \t]*=[ \t]*' $lxcpath/$guest/config | \ + devices=`grep '^lxc\.network\.veth\.pair[ \t]*=[ \t]*' $lxcpath/$guest/config | \ awk '{ split($0, a, /=/); gsub(/[ \t]/, "", a[2]); print a[2]; }'` - if [ -n "$device" ]; then - device_re=`echo $device | sed -e 's/\./\\\\./g'` - if [ `grep -c "^ *$device_re:" /proc/net/dev` -eq 1 ]; then - actives="$actives $guest" - eval "dev_$(clean_fieldname $guest)=$device" - fi + if [ -n "$devices" ]; then + for device in $devices; do + device_re=`echo $device | sed -e 's/\./\\\\./g'` + if [ `grep -c "^ *$device_re:" /proc/net/dev` -eq 1 ]; then + actives="$actives $guest" + eval "dev_$(clean_fieldname $guest)=$device" + fi + done fi fi done @@ -82,7 +84,7 @@ if [ "$1" = "config" ]; then echo "graph_title Network traffic" echo "graph_args --base 1000" echo "graph_vlabel bits in (-) / out (+) per ${graph_period}" - echo "graph_category lxc" + echo "graph_category network" echo "graph_info This graph shows the traffic of active LXC containers." for guestname in $actives; do diff --git a/plugins/lxc/lxc_proc b/plugins/lxc/lxc_proc index d9cd1636..c2e3b02d 100755 --- a/plugins/lxc/lxc_proc +++ b/plugins/lxc/lxc_proc @@ -93,7 +93,7 @@ if [ "$1" = "config" ]; then echo 'graph_title Processes ' echo 'graph_args -l 0 --base 1000' echo 'graph_vlabel Number of processes' - echo 'graph_category lxc' + echo 'graph_category processes' for guest_name in $guest_names; diff --git a/plugins/lxc/lxc_ram b/plugins/lxc/lxc_ram index 1d61b2c9..db770778 100755 --- a/plugins/lxc/lxc_ram +++ b/plugins/lxc/lxc_ram @@ -60,7 +60,7 @@ if [ "$1" = "config" ]; then echo 'graph_title Memory ' echo 'graph_args -l 0 --base 1024' echo 'graph_vlabel byte' - echo 'graph_category lxc' + echo 'graph_category memory' for guest_name in $guest_names; diff --git a/plugins/lxd/lxd_disk b/plugins/lxd/lxd_disk new file mode 100755 index 00000000..7a58993a --- /dev/null +++ b/plugins/lxd/lxd_disk @@ -0,0 +1,56 @@ +#!/usr/bin/python3 + +import sys + +errors=[] + +HAS_LIB=True +try: + from pylxd import api +except: + HAS_LIB=False + errors.append("no pylxd module") + +c=None +HAS_ACCESS=True +try: + c=api.API() + c.container_list() +except: + HAS_ACCESS=False + errors.append("no socket access") + +if len(sys.argv) == 2 and sys.argv[1]=="autoconf": + if HAS_LIB and HAS_ACCESS: + print("yes") + else: + print("no ("+" and ".join(errors)+")") + sys.exit(0) + +if not (HAS_LIB and HAS_ACCESS): + # pylxd not installed or lxd socket not accessible + sys.exit(1) + +if len(sys.argv) == 2 and sys.argv[1]=="config": + print("graph_title LXD container disk usage") + print("graph_args --base 1000 --lower-limit 0") + print("graph_vlabel Bytes") + print("graph_category virtualization") + print("graph_info This shows the disk usage of storage in containers. Make sure to install pylxd in python3.") + for name in c.container_list(): + info = c.container_info(name) + # first check if disk information list is available or None + if info['disk']: + # if no disk information is present, this would fail to iteration None + for disk in info['disk']: + print(name+"-"+disk+".label "+name) + print(name+"-"+disk+".draw LINE2") + sys.exit(0) + +for name in c.container_list(): + info = c.container_info(name) + # first check if disk information list is available or None + if info['disk']: + # if no disk information is present, this would fail to iteration None + for disk in info['disk']: + print(name+"-"+disk+".value "+str(info['disk'][disk]['usage'])) diff --git a/plugins/lxd/lxd_mem b/plugins/lxd/lxd_mem new file mode 100755 index 00000000..f81a767d --- /dev/null +++ b/plugins/lxd/lxd_mem @@ -0,0 +1,46 @@ +#!/usr/bin/python3 + +import sys + +errors=[] + +HAS_LIB=True +try: + from pylxd import api +except: + HAS_LIB=False + errors.append("no pylxd module") + +c=None +HAS_ACCESS=True +try: + c=api.API() + c.container_list() +except: + HAS_ACCESS=False + errors.append("no socket access") + +if len(sys.argv) == 2 and sys.argv[1]=="autoconf": + if HAS_LIB and HAS_ACCESS: + print("yes") + else: + print("no ("+" and ".join(errors)+")") + sys.exit(0) + +if not (HAS_LIB and HAS_ACCESS): + # pylxd not installed or lxd socket not accessible + sys.exit(1) + +if len(sys.argv) == 2 and sys.argv[1]=="config": + print("graph_title LXD container memory") + print("graph_args --base 1024 --lower-limit 0") + print("graph_vlabel Bytes") + print("graph_category virtualization") + print("graph_info This shows the memory usage of each container. Make sure to install pylxd in python3.") + for name in c.container_list(): + print(name+".label "+name) + print(name+".draw AREASTACK") + sys.exit(0) + +for name in c.container_list(): + print(name+".value "+str(c.container_info(name)['memory']['usage'])) diff --git a/plugins/mail/dovecot b/plugins/mail/dovecot deleted file mode 100755 index e5a6d1d1..00000000 --- a/plugins/mail/dovecot +++ /dev/null @@ -1,126 +0,0 @@ -#! /bin/bash -# -# Munin Plugin -# to count logins to your dovecot mailserver -# -# Created by Dominik Schulz -# http://developer.gauner.org/munin/ -# Contributions by: -# - Stephane Enten -# - Steve Schnepp -# -# 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 -###################### -EXPR_BIN=/usr/bin/expr -LOGFILE=${logfile:-/var/log/mail.log} -###################### - -if [ "$1" = "autoconf" ]; then - echo yes - exit 0 -fi - -if [ "$1" = "config" ]; then - echo 'graph_title Dovecot Logins' - echo 'graph_category Mail' - echo 'graph_args --base 1000 -l 0' - echo 'graph_vlabel Login Counters' - - for t in Total TLS SSL IMAP POP3 - do - field=$(echo $t | tr '[:upper:]' '[:lower:]') - echo "login_$field.label $t Logins" - echo "login_$field.type DERIVE" - echo "login_$field.min 0" - done - - echo 'connected.label Connected Users' - - exit 0 -fi - -###################### -# Total Logins -###################### -echo -en "login_total.value " -VALUE=$(egrep -c '[dovecot]?.*Login' $LOGFILE) -if [ ! -z "$VALUE" ]; then - echo "$VALUE" -else - echo "0" -fi -echo -n -###################### -# Connected Users -###################### -DISCONNECTS=$(egrep -c '[dovecot]?.*Disconnected' $LOGFILE) -CONNECTS=$(egrep -c '[dovecot]?.*Login' $LOGFILE) -VALUE=$($EXPR_BIN $CONNECTS - $DISCONNECTS) -if [ -z "$VALUE" ] || [ "$VALUE" -lt 0 ]; then - VALUE=0 -fi -echo -en "connected.value " -echo $VALUE -echo -n -###################### -# TLS Logins -###################### -echo -en "login_tls.value " -VALUE=$(egrep -c '[dovecot]?.*Login.*TLS' $LOGFILE) -if [ ! -z "$VALUE" ]; then - echo "$VALUE" -else - echo "0" -fi -echo -n -###################### -# SSL Logins -###################### -echo -en "login_ssl.value " -VALUE=$(egrep -c '[dovecot]?.*Login.*SSL' $LOGFILE) -if [ ! -z "$VALUE" ]; then - echo "$VALUE" -else - echo "0" -fi -echo -n -###################### -# IMAP Logins -###################### -echo -en "login_imap.value " -VALUE=$(egrep -c '[dovecot]?.*imap.*Login' $LOGFILE) -if [ ! -z "$VALUE" ]; then - echo "$VALUE" -else - echo "0" -fi -echo -n -###################### -# POP3 Logins -###################### -echo -en "login_pop3.value " -VALUE=$(egrep -c '[dovecot]?.*pop3.*Login' $LOGFILE) -if [ ! -z "$VALUE" ]; then - echo "$VALUE" -else - echo "0" -fi -echo -n diff --git a/plugins/mail/imap_bandwidth b/plugins/mail/imap_bandwidth index fc5cd894..be8084b1 100755 --- a/plugins/mail/imap_bandwidth +++ b/plugins/mail/imap_bandwidth @@ -48,7 +48,7 @@ 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. +IMAP download of a file containing random bytes. You need to be aware that this measurement obviously increases the load on your IMAP servers for the duration of the measurement. diff --git a/plugins/mail/nullmailer_queue b/plugins/mail/nullmailer_queue new file mode 100755 index 00000000..65806b88 --- /dev/null +++ b/plugins/mail/nullmailer_queue @@ -0,0 +1,89 @@ +#!/bin/sh + +: <<=cut + +=head1 NAME + +nullmailer_queue - monitor for nullmailer's send queue. + +=head1 APPLICABLE SYSTEMS + +This plugin is applicable to any system running the nullmailer MTA. + +=head1 CONFIGURATION + +This plugin needs to be able to read the nullmailer queue, so it +will need to run as that user or root. + +Additionally, the nullmailer queue directory may vary; in that case you +can specify it with the "queuedir" and "errordir" environment variables. + +Example: + +[nullmailer_queue] +user nullmail +env.queuedir /var/spool/nullmailer/queue +env.errordir /var/spool/nullmailer/failed + +=head1 INTERPRETATION + +This plugin will draw a stack of 2: the number of emails in the queue, and +the number of emails that have permanently failed sending. The latter has +a warning attached, since those should never happen. + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=head1 VERSION + + 1.0.0 + +=head1 AUTHOR + +Bert Peters + +=head1 LICENSE + +GPLv2 + +=cut + +queuedir=${queuedir:-/var/spool/nullmailer/queue} +errordir=${errordir:-/var/spool/nullmailer/failed} + +case $1 in + autoconf) + if command -v nullmailer-queue >/dev/null 2>/dev/null; then + [ -r "$queuedir" ] && echo yes || echo "no (queue dir not readable)" + else + echo "no (nullmailer not installed)" + fi + ;; + + config) + cat <<-EOF +graph_args -l 0 +graph_title Nullmailer queue +graph_vlabel emails +graph_total total +graph_category mail + +queue.label queued +queue.draw AREASTACK +queue.info Number of emails currently in the queue + +failed.label failed +failed.draw AREASTACK +failed.warning 0:0 +failed.info Number of emails that have permanently failed to send +EOF + ;; + + *) + echo "queue.value $(find "$queuedir" -type f | wc -l)" + # Failed does not exist until there has been a failure, so mute the "file not found" + echo "failed.value $(find "$errordir" -type f 2> /dev/null | wc -l)" + ;; +esac diff --git a/plugins/mail/postfix_stats b/plugins/mail/postfix_stats deleted file mode 100755 index 7a2bcd5b..00000000 --- a/plugins/mail/postfix_stats +++ /dev/null @@ -1,52 +0,0 @@ -#!/bin/sh -# -# Plugin to show Postfix statistics - needs pflogsumm -# -# Contributed by David Obando (david@cryptix.de) - 16.04.2007 -# -# -# Magic markers - optional - used by installation scripts and -# munin-config: -# -#%# family=manual -#%# capabilities=autoconf - -#set -xv - -case $1 in - config) - cat <<'EOF' -system.type COUNTER -graph_title Postfix statistics -graph_vlabel Postfix statistics -graph_category Mail -graph_total Total -received.label received -delivered.label delivered -forwarded.label forwarded -deferred.label deferred -bounced.label bounced -rejected.label rejected -held.label held -discarded.label discarded -EOF - exit 0;; -esac - - -TMP=`mktemp /tmp/tmp.XXXXXXXX` -pflogsumm.pl --smtpd_stats -d today /var/log/syslog /var/log/syslog.0 | head -n 15 > $TMP - -cat < $ENV{LOGFILE} || '/home/user/.procmail/log', - state => $ENV{STATEFILE} || '/var/lib/munin/plugin-state/munin-plugin-procmail.state'); + state => $ENV{STATEFILE} || "$ENV{MUNIN_PLUGSTATE}/munin-plugin-procmail.state"); $state = YAML::LoadFile($conf{state}); $have_read = 0; diff --git a/plugins/mail/mailman-queue-check b/plugins/mailman/mailman-queue-check similarity index 96% rename from plugins/mail/mailman-queue-check rename to plugins/mailman/mailman-queue-check index 4c75c792..3a9deab3 100755 --- a/plugins/mail/mailman-queue-check +++ b/plugins/mailman/mailman-queue-check @@ -17,7 +17,7 @@ if [ "$1" = "config" ]; then echo "graph_title Mailman Queue" - echo "graph_category mailman" + echo "graph_category mailinglist" echo "graph_args --base 1000 -l 0" echo "archive.label Archive" echo "archive.draw LINE2" diff --git a/plugins/mail/mailman_subscribers b/plugins/mailman/mailman_subscribers similarity index 98% rename from plugins/mail/mailman_subscribers rename to plugins/mailman/mailman_subscribers index 85181e73..a5809d4f 100755 --- a/plugins/mail/mailman_subscribers +++ b/plugins/mailman/mailman_subscribers @@ -58,7 +58,7 @@ if ($ARGV[0] and $ARGV[0] eq "config" ){ print "graph_args --base 1000 -l 0\n"; print "graph_scale yes\n"; print "graph_vlabel subscribers\n"; - print "graph_category mailman\n"; + print "graph_category mailinglist\n"; print "graph_total Total\n"; print 'graph_info Plugin available at http://rodolphe.quiedeville.org/hack/munin/mailman/'."\n"; diff --git a/plugins/wiki/mediawiki b/plugins/mediawiki/mediawiki similarity index 100% rename from plugins/wiki/mediawiki rename to plugins/mediawiki/mediawiki diff --git a/plugins/wiki/mediawiki_api b/plugins/mediawiki/mediawiki_api old mode 100644 new mode 100755 similarity index 100% rename from plugins/wiki/mediawiki_api rename to plugins/mediawiki/mediawiki_api diff --git a/plugins/memcached/memcached_bytes_ b/plugins/memcached/memcached_bytes_ index 34be30bc..c9aeefb3 100755 --- a/plugins/memcached/memcached_bytes_ +++ b/plugins/memcached/memcached_bytes_ @@ -11,7 +11,7 @@ if ($cmd eq 'config') { print "graph_title Memcached bytes used\n"; print "graph_args --base 1024 -l 0\n"; print "graph_vlabel bytes\n"; - print "graph_category memcached\n"; + print "graph_category memory\n"; print "graph_info This graph monitors the size of the memcached cache.\n"; print "bytes.label bytes used\n"; print "bytes.info Number of bytes currently used\n"; diff --git a/plugins/memcached/memcached_bytes_all b/plugins/memcached/memcached_bytes_all index 72a5d679..7b7bbe95 100755 --- a/plugins/memcached/memcached_bytes_all +++ b/plugins/memcached/memcached_bytes_all @@ -40,7 +40,7 @@ if ($cmd eq 'config') { print "graph_title Memcached bytes used for all instances\n"; print "graph_args --base 1024 -l 0\n"; print "graph_vlabel bytes\n"; - print "graph_category memcached\n"; + print "graph_category memory\n"; print "graph_info This graph monitors the size of the memcached cache for every instance.\n"; foreach my $instance_name (keys(%instances)) { diff --git a/plugins/memcached/memcached_connections_ b/plugins/memcached/memcached_connections_ index 55c3e051..068707ab 100755 --- a/plugins/memcached/memcached_connections_ +++ b/plugins/memcached/memcached_connections_ @@ -11,7 +11,7 @@ if ($cmd eq 'config') { print "graph_title Memcached connections\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel connections\n"; - print "graph_category memcached\n"; + print "graph_category memory\n"; print "graph_info This graph monitors the connections to the memcached server.\n"; print "connections.label connections\n"; print "connections.info Number of connections to memcached\n"; diff --git a/plugins/memcached_ext/memcached_ext_bytes_ b/plugins/memcached/memcached_ext_bytes_ similarity index 97% rename from plugins/memcached_ext/memcached_ext_bytes_ rename to plugins/memcached/memcached_ext_bytes_ index 10642a94..8dda1871 100755 --- a/plugins/memcached_ext/memcached_ext_bytes_ +++ b/plugins/memcached/memcached_ext_bytes_ @@ -34,7 +34,7 @@ if ($cmd eq 'config') { print "graph_title Memcached bytes used on $label\n"; print "graph_args --base 1024 -l 0\n"; print "graph_vlabel bytes\n"; - print "graph_category memcached\n"; + print "graph_category memory\n"; print "graph_info This graph monitors the size of the memcached cache.\n"; print "bytes.label bytes used\n"; print "bytes.info Number of bytes currently used\n"; diff --git a/plugins/memcached_ext/memcached_ext_connections_ b/plugins/memcached/memcached_ext_connections_ similarity index 97% rename from plugins/memcached_ext/memcached_ext_connections_ rename to plugins/memcached/memcached_ext_connections_ index 57d1be7c..326c3afc 100755 --- a/plugins/memcached_ext/memcached_ext_connections_ +++ b/plugins/memcached/memcached_ext_connections_ @@ -34,7 +34,7 @@ if ($cmd eq 'config') { print "graph_title Memcached connections on $label\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel connections\n"; - print "graph_category memcached\n"; + print "graph_category memory\n"; print "graph_info This graph monitors the connections to the memcached server.\n"; print "connections.label connections\n"; print "connections.info Number of connections to memcached\n"; diff --git a/plugins/memcached_ext/memcached_ext_hits_ b/plugins/memcached/memcached_ext_hits_ similarity index 97% rename from plugins/memcached_ext/memcached_ext_hits_ rename to plugins/memcached/memcached_ext_hits_ index d2a7724d..2aca77a5 100755 --- a/plugins/memcached_ext/memcached_ext_hits_ +++ b/plugins/memcached/memcached_ext_hits_ @@ -36,7 +36,7 @@ if ($cmd eq 'config') { print "graph_title Memcached cache hits and misses on $label\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel requests\n"; - print "graph_category memcached\n"; + print "graph_category memory\n"; print "graph_info This graph monitors the number of cache hits and misses.\n"; print "hits.label hits\n"; print "hits.info Number of cache hits\n"; diff --git a/plugins/memcached_ext/memcached_ext_items_ b/plugins/memcached/memcached_ext_items_ similarity index 97% rename from plugins/memcached_ext/memcached_ext_items_ rename to plugins/memcached/memcached_ext_items_ index 39b56503..19e359f4 100755 --- a/plugins/memcached_ext/memcached_ext_items_ +++ b/plugins/memcached/memcached_ext_items_ @@ -35,7 +35,7 @@ if ($cmd eq 'config') { print "graph_title Memcached cached items on $label\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel items\n"; - print "graph_category memcached\n"; + print "graph_category memory\n"; print "graph_info This graph monitors the number of items stored by the memcached server.\n"; print "items.label items\n"; print "items.info Number of cached items\n"; diff --git a/plugins/memcached_ext/memcached_ext_requests_ b/plugins/memcached/memcached_ext_requests_ similarity index 97% rename from plugins/memcached_ext/memcached_ext_requests_ rename to plugins/memcached/memcached_ext_requests_ index b7b75fce..9c1d9017 100755 --- a/plugins/memcached_ext/memcached_ext_requests_ +++ b/plugins/memcached/memcached_ext_requests_ @@ -35,7 +35,7 @@ if ($cmd eq 'config') { print "graph_title Memcached requests on $label\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel requests\n"; - print "graph_category memcached\n"; + print "graph_category memory\n"; print "graph_info This graph monitors the number of get and set requests.\n"; print "gets.label gets\n"; print "gets.info Number of get requests\n"; diff --git a/plugins/memcached_ext/memcached_ext_traffic_ b/plugins/memcached/memcached_ext_traffic_ similarity index 97% rename from plugins/memcached_ext/memcached_ext_traffic_ rename to plugins/memcached/memcached_ext_traffic_ index 640efc43..a9354ca0 100755 --- a/plugins/memcached_ext/memcached_ext_traffic_ +++ b/plugins/memcached/memcached_ext_traffic_ @@ -35,7 +35,7 @@ if ($cmd eq 'config') { print "graph_title Memcached network traffic on $label\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel bits per \${graph_period}\n"; - print "graph_category memcached\n"; + print "graph_category memory\n"; print "graph_info This graph monitors the network traffic of the memcached server.\n"; print "up.label bits in\n"; print "up.info Traffic received by memcached\n"; diff --git a/plugins/memcached/memcached_hits_ b/plugins/memcached/memcached_hits_ index d6697728..c0cdc5e2 100755 --- a/plugins/memcached/memcached_hits_ +++ b/plugins/memcached/memcached_hits_ @@ -18,7 +18,7 @@ if ($cmd eq 'config') { print "graph_title Memcached cache hits and misses -- $title\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel requests\n"; - print "graph_category memcached\n"; + print "graph_category memory\n"; print "graph_info This graph monitors the number of cache hits and misses.\n"; print "hits.label hits\n"; print "hits.info Number of cache hits\n"; diff --git a/plugins/memcached/memcached_items_ b/plugins/memcached/memcached_items_ index a8de9c26..92b6cf9f 100755 --- a/plugins/memcached/memcached_items_ +++ b/plugins/memcached/memcached_items_ @@ -11,7 +11,7 @@ if ($cmd eq 'config') { print "graph_title Memcached cached items\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel items\n"; - print "graph_category memcached\n"; + print "graph_category memory\n"; print "graph_info This graph monitors the number of items stored by the memcached server.\n"; print "items.label items\n"; print "items.info Number of cached items\n"; diff --git a/plugins/memcached/memcached_multi_ b/plugins/memcached/memcached_multi_ index e18c7315..f38affa0 100755 --- a/plugins/memcached/memcached_multi_ +++ b/plugins/memcached/memcached_multi_ @@ -183,7 +183,7 @@ $graphs{items} = { config => { args => '--base 1000 --lower-limit 0', vlabel => 'Items in Memcached', - category => 'memcached global items', + category => 'memory', title => 'Items', info => 'Number of items in use by memcached', }, @@ -197,7 +197,7 @@ $graphs{memory} = { config => { args => '--base 1024 --lower-limit 0', vlabel => 'Bytes Used', - category => 'memcached global memory', + category => 'memory', title => 'Memory Usage', info => 'Memory consumption of memcached', }, @@ -212,7 +212,7 @@ $graphs{bytes} = { args => '--base 1000', vlabel => 'bits in (-) / out (+)', title => 'Network Traffic', - category => 'memcached', + category => 'memory', info => 'Network traffic in (-) / out (+) of the machine', order => 'bytes_read bytes_written', }, @@ -228,7 +228,7 @@ $graphs{conns} = { config => { args => '--base 1000 --lower-limit 0', vlabel => 'Connections per ${graph_period}', - category => 'memcached', + category => 'memory', title => 'Connections', info => 'Number of connections being handled by memcached', order => 'max_conns curr_conns avg_conns', @@ -244,7 +244,7 @@ $graphs{commands} = { config => { args => '--base 1000 --lower-limit 0', vlabel => 'Commands per ${graph_period}', - category => 'memcached global commands', + category => 'memory', title => 'Commands', info => 'Number of commands being handled by memcached', }, @@ -290,7 +290,7 @@ $graphs{evictions} = { config => { args => '--base 1000 --lower-limit 0', vlabel => 'Evictions per ${graph_period}', - category => 'memcached global evictions', + category => 'memory', title => 'Evictions', info => 'Number of evictions per second', }, @@ -308,7 +308,7 @@ $graphs{unfetched} = { config => { args => '--base 1000 --lower-limit 0', vlabel => 'Unfetched Items per ${graph_period}', - category => 'memcached global unfetched', + category => 'memory', title => 'Unfetched Items', info => 'Number of items that were never touched get/incr/append/etc before X occurred', }, @@ -324,7 +324,7 @@ $graphs{slabchnks} = { config => { args => '--base 1000 --lower-limit 0', vlabel => 'Available Chunks for this Slab', - category => 'memcached slab chunk usage', + category => 'memory', title => 'Chunk Usage for Slab: ', info => 'This graph shows you the chunk usage for this memory slab.', }, @@ -339,7 +339,7 @@ $graphs{slabhits} = { config => { args => '--base 1000 --lower-limit 0', vlabel => 'Hits per Slab per ${graph_period}', - category => 'memcached slab commands', + category => 'memory', title => 'Hits for Slab: ', info => 'This graph shows you the successful hit rate for this memory slab.', }, @@ -359,7 +359,7 @@ $graphs{slabevics} = { config => { args => '--base 1000 --lower-limit 0', vlabel => 'Evictions per Slab per ${graph_period}', - category => 'memcached slab evictions', + category => 'memory', title => 'Evictions for Slab: ', info => 'This graph shows you the eviction rate for this memory slab.', }, @@ -377,7 +377,7 @@ $graphs{slabevictime} = { config => { args => '--base 1000 --lower-limit 0', vlabel => ' since Request for LEI', - category => 'memcached slab eviction time', + category => 'memory', title => 'Eviction Request Time for Slab: ', info => 'This graph shows you the time since we requested the last evicted item', }, @@ -391,7 +391,7 @@ $graphs{slabitems} = { config => { args => '--base 1000 --lower-limit 0', vlabel => 'Items per Slab', - category => 'memcached slab item count', + category => 'memory', title => 'Items in Slab: ', info => 'This graph shows you the number of items and reclaimed items per slab.', }, @@ -405,7 +405,7 @@ $graphs{slabitemtime} = { config => { args => '--base 1000 --lower-limit 0', vlabel => ' since item was stored', - category => 'memcached slab item age', + category => 'memory', title => 'Age of Eldest Item in Slab: ', info => 'This graph shows you the time of the eldest item in this slab', }, @@ -418,7 +418,7 @@ $graphs{slabunfetched} = { config => { args => '--base 1000 --lower-limit 0', vlabel => 'Unfetched Items per ${graph_period}', - category => 'memcached slab unfetched', + category => 'memory', title => 'Unfetched Items in Slab: ', info => 'Number of items that were never touched get/incr/append/etc before X occurred', }, @@ -863,7 +863,7 @@ sub print_rootmulti_config { # Lets tell munin about the graph we are referencing and print the main config print "multigraph memcached_multi_$plugin\n"; while ( my ($key, $value) = each(%graphconf)) { - if ($key eq 'category') { $value = 'memcached' }; + if ($key eq 'category') { $value = 'memory' }; print "graph_$key $value\n"; } # Lets tell munin about our data values and how to treat them diff --git a/plugins/memcached/memcached_requests_ b/plugins/memcached/memcached_requests_ index b414b674..47d7a7f5 100755 --- a/plugins/memcached/memcached_requests_ +++ b/plugins/memcached/memcached_requests_ @@ -18,7 +18,7 @@ if ($cmd eq 'config') { print "graph_title Memcached requests -- $title\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel requests\n"; - print "graph_category memcached\n"; + print "graph_category memory\n"; print "graph_info This graph monitors the number of get and set requests.\n"; print "gets.label gets\n"; print "gets.info Number of get requests\n"; diff --git a/plugins/memcached/memcached_servers_ b/plugins/memcached/memcached_servers_ old mode 100644 new mode 100755 index f1262035..bf5dfcd2 --- a/plugins/memcached/memcached_servers_ +++ b/plugins/memcached/memcached_servers_ @@ -72,7 +72,7 @@ if ($#addresses < 0 || $#labels < 0) { } if ($#addresses != $#labels) { - print "Error: number of adresses and labels must be equal.\n"; + print "Error: number of addresses and labels must be equal.\n"; exit 1; } @@ -93,31 +93,31 @@ if ($cmd eq 'config') { print "graph_title Memcached bytes used\n"; print "graph_args --base 1024 -l 0\n"; print "graph_vlabel bytes\n"; - print "graph_category memcached\n"; + print "graph_category memory\n"; print "graph_info This graph monitors the size of the memcached cache.\n"; } elsif ($mode eq 'hits') { print "graph_title Memcached cache hits and misses\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel requests\n"; - print "graph_category memcached\n"; + print "graph_category memory\n"; print "graph_info This graph monitors the number of cache hits and misses.\n"; } elsif ($mode eq 'items') { print "graph_title Memcached cached items\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel items\n"; - print "graph_category memcached\n"; + print "graph_category memory\n"; print "graph_info This graph monitors the number of items stored by the memcached server.\n"; } elsif ($mode eq 'requests') { print "graph_title Memcached requests\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel requests\n"; - print "graph_category memcached\n"; + print "graph_category memory\n"; print "graph_info This graph monitors the number of get and set requests.\n"; } elsif ($mode eq 'traffic') { print "graph_title Memcached network traffic\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel bits per \${graph_period}\n"; - print "graph_category memcached\n"; + print "graph_category memory\n"; print "graph_info This graph monitors the network traffic of the memcached server.\n"; } diff --git a/plugins/memcached/memcached_traffic_ b/plugins/memcached/memcached_traffic_ index b30eee0d..a47cb40a 100755 --- a/plugins/memcached/memcached_traffic_ +++ b/plugins/memcached/memcached_traffic_ @@ -11,7 +11,7 @@ if ($cmd eq 'config') { print "graph_title Memcached network traffic\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel bits per \${graph_period}\n"; - print "graph_category memcached\n"; + print "graph_category memory\n"; print "graph_info This graph monitors the network traffic of the memcached server.\n"; print "up.label bits in\n"; print "up.info Traffic received by memcached\n"; diff --git a/plugins/system/kmemsum b/plugins/memory/kmemsum similarity index 100% rename from plugins/system/kmemsum rename to plugins/memory/kmemsum diff --git a/plugins/system/memory_by_process b/plugins/memory/memory_by_process similarity index 84% rename from plugins/system/memory_by_process rename to plugins/memory/memory_by_process index b06e3391..54d07a83 100755 --- a/plugins/system/memory_by_process +++ b/plugins/memory/memory_by_process @@ -21,7 +21,7 @@ if [ "$1" = "config" ]; then echo "graph_title VM by Process" echo 'graph_args --base 1024k' echo 'graph_vlabel VM size' - echo 'graph_category system' + echo 'graph_category processes' echo "graph_info Shows contribution of each process to VM size" ps auxww | perl -e ' @@ -30,18 +30,21 @@ if [ "$1" = "config" ]; then { @a = split; $proc = $a[10]; - $proc =~ s|.*/||; + $label = $proc; + $proc =~ s/ .*//; + if($proc =~ /^\[/) { $proc =~ s|/.*||; } else { $proc =~ s|.*/||; } $proc =~ s/:.*//; $proc =~ tr/[]//d; $proc =~ tr/A-Za-z0-9/_/c; $vsz = $a[4]; $total{$proc} += $vsz; + $labels{$proc} = $label; } my $stack = 0; sub draw() { return $stack++ ? "STACK" : "AREA" } print map { - "$_.label $_\n" . + "$_.label " . $labels{$_} . "\n" . "$_.min 0\n" . "$_.draw " . draw() . "\n" . "$_.cdef $_,1024,*\n" @@ -56,7 +59,7 @@ else { @a = split; $proc = $a[10]; - $proc =~ s|.*/||; + if($proc =~ /^\[/) { $proc =~ s|/.*||; } else { $proc =~ s|.*/||; } $proc =~ s/:.*//; $proc =~ tr/[]//d; $proc =~ tr/A-Za-z0-9/_/c; diff --git a/plugins/processes/multimemory b/plugins/memory/multimemory similarity index 100% rename from plugins/processes/multimemory rename to plugins/memory/multimemory diff --git a/plugins/processes/proc_mem b/plugins/memory/proc_mem similarity index 100% rename from plugins/processes/proc_mem rename to plugins/memory/proc_mem diff --git a/plugins/system/proc_memory_status b/plugins/memory/proc_memory_status similarity index 98% rename from plugins/system/proc_memory_status rename to plugins/memory/proc_memory_status index 7c10cecd..3ad2752c 100755 --- a/plugins/system/proc_memory_status +++ b/plugins/memory/proc_memory_status @@ -28,7 +28,7 @@ if [ "$1" = "config" ]; then echo 'graph_vlabel Ko' echo 'graph_scale no' echo "graph_info This graph display memory usage for a process" - echo 'graph_category system' + echo 'graph_category processes' echo 'graph_period second' echo 'VmExe.label VmExe' diff --git a/plugins/minecraft/bukkit-jsonapi-players b/plugins/minecraft/bukkit-jsonapi-players old mode 100644 new mode 100755 index 69cd5ba0..adf8710d --- a/plugins/minecraft/bukkit-jsonapi-players +++ b/plugins/minecraft/bukkit-jsonapi-players @@ -32,7 +32,7 @@ $port = 20059; if ((count($argv) > 1) && ($argv[1] == 'config')) { print("graph_title Bukkit / JSONAPI - players online -graph_category bukkit +graph_category games graph_vlabel players graph_args --base 1000 -l 0 players.type GAUGE diff --git a/plugins/minecraft/bukkit-jsonapi-ramusage b/plugins/minecraft/bukkit-jsonapi-ramusage old mode 100644 new mode 100755 index 64e5f243..4de54e93 --- a/plugins/minecraft/bukkit-jsonapi-ramusage +++ b/plugins/minecraft/bukkit-jsonapi-ramusage @@ -32,7 +32,7 @@ $port = 20059; if ((count($argv) > 1) && ($argv[1] == 'config')) { print("graph_title Bukkit / JSONAPI - RAM usage -graph_category bukkit +graph_category games graph_vlabel RAM usage in GB graph_args --base 1024 -l 0 total.label total diff --git a/plugins/minecraft/bukkit-jsonapi-tps b/plugins/minecraft/bukkit-jsonapi-tps old mode 100644 new mode 100755 index cf3b31db..597861ff --- a/plugins/minecraft/bukkit-jsonapi-tps +++ b/plugins/minecraft/bukkit-jsonapi-tps @@ -32,7 +32,7 @@ $port = 20059; if ((count($argv) > 1) && ($argv[1] == 'config')) { print("graph_title Bukkit / JSONAPI - ticks per second (TPS) -graph_category bukkit +graph_category games graph_vlabel ticks per second graph_args --base 1000 -l 0 tps.type GAUGE diff --git a/plugins/minecraft/bukkit-statistician-killshostile b/plugins/minecraft/bukkit-statistician-killshostile old mode 100644 new mode 100755 index de8073dc..0e4640e1 --- a/plugins/minecraft/bukkit-statistician-killshostile +++ b/plugins/minecraft/bukkit-statistician-killshostile @@ -33,7 +33,7 @@ $port = 3306; if ((count($argv) > 1) && ($argv[1] == 'config')) { print("graph_title Bukkit / Statistician - hostile mob kills per day -graph_category bukkit +graph_category games graph_vlabel hostile mob kills per day graph_args --base 1000 -l 0 blaze.type GAUGE diff --git a/plugins/minecraft/bukkit-statistician-killsneutral b/plugins/minecraft/bukkit-statistician-killsneutral old mode 100644 new mode 100755 index f93aa114..cfa85f56 --- a/plugins/minecraft/bukkit-statistician-killsneutral +++ b/plugins/minecraft/bukkit-statistician-killsneutral @@ -33,7 +33,7 @@ $port = 3306; if ((count($argv) > 1) && ($argv[1] == 'config')) { print("graph_title Bukkit / Statistician - neutral mob kills per day -graph_category bukkit +graph_category games graph_vlabel neutral mob kills per day graph_args --base 1000 -l 0 enderman.type GAUGE diff --git a/plugins/minecraft/bukkit-statistician-killspassive b/plugins/minecraft/bukkit-statistician-killspassive old mode 100644 new mode 100755 index 5eb7df88..8344ab15 --- a/plugins/minecraft/bukkit-statistician-killspassive +++ b/plugins/minecraft/bukkit-statistician-killspassive @@ -33,7 +33,7 @@ $port = 3306; if ((count($argv) > 1) && ($argv[1] == 'config')) { print("graph_title Bukkit / Statistician - passive mob kills per day -graph_category bukkit +graph_category games graph_vlabel passive mob kills per day graph_args --base 1000 -l 0 bat.type GAUGE diff --git a/plugins/minecraft/bukkit-statistician-players b/plugins/minecraft/bukkit-statistician-players old mode 100644 new mode 100755 index 291251ed..1b0b3399 --- a/plugins/minecraft/bukkit-statistician-players +++ b/plugins/minecraft/bukkit-statistician-players @@ -33,7 +33,7 @@ $port = 3306; if ((count($argv) > 1) && ($argv[1] == 'config')) { print("graph_title Bukkit / Statistician - new players per day -graph_category bukkit +graph_category games graph_vlabel new players per day graph_args --base 1000 -l 0 players.type GAUGE diff --git a/plugins/minecraft/bukkit-ultrabans-shame b/plugins/minecraft/bukkit-ultrabans-shame old mode 100644 new mode 100755 index a14c7118..2ce91192 --- a/plugins/minecraft/bukkit-ultrabans-shame +++ b/plugins/minecraft/bukkit-ultrabans-shame @@ -34,7 +34,7 @@ $port = 3306; if ((count($argv) > 1) && ($argv[1] == 'config')) { print("graph_title Bukkit / Ultrabans - shame per day -graph_category bukkit +graph_category games graph_vlabel amount of shame per day graph_args --base 1000 -l 0 unban.type GAUGE diff --git a/plugins/minecraft/minecraft-users b/plugins/minecraft/minecraft-users index 7e4861f3..ecfa641b 100755 --- a/plugins/minecraft/minecraft-users +++ b/plugins/minecraft/minecraft-users @@ -12,7 +12,7 @@ if ARGV[0] == 'config' puts "graph_vlabel players" puts "players.label players" puts "graph_info Number of players connected to Minecraft" - puts "graph_category Minecraft" + puts "graph_category games" exit end @@ -33,4 +33,4 @@ 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 +puts "players.value #{player_count}" diff --git a/plugins/minecraft/minecraft-users-ram_ b/plugins/minecraft/minecraft-users-ram_ old mode 100644 new mode 100755 index d7cc2953..9db52074 --- a/plugins/minecraft/minecraft-users-ram_ +++ b/plugins/minecraft/minecraft-users-ram_ @@ -8,6 +8,7 @@ MC_PORT=${0##*_} if [ "$1" = "config" ] then printf 'graph_title Minecraft-Server (Port %s)\n' ${MC_PORT} + printf 'graph_category games' printf 'graph_vlabel Anzahl\n' printf 'users.label Benutzer\n' printf 'ramusage.label Verwendeter RAM in GiB\n' diff --git a/plugins/mail/mixminion b/plugins/mixminion/mixminion similarity index 100% rename from plugins/mail/mixminion rename to plugins/mixminion/mixminion diff --git a/plugins/moblock/moblock_connections b/plugins/moblock/moblock_connections index c6bcd26f..6f163e9c 100755 --- a/plugins/moblock/moblock_connections +++ b/plugins/moblock/moblock_connections @@ -32,7 +32,7 @@ def config puts "graph_args --base 1000 -r --lower-limit 0" puts "graph_title Moblock" puts "graph_vlabel Blocked Connections" - puts "graph_category moblock" + puts "graph_category fw" puts "graph_info This graph shows the number of connections blocked by Moblock" puts "blocked_in.label Blocked In" diff --git a/plugins/mod_jk/mod_jk b/plugins/mod_jk/mod_jk index 7c607d36..f4358c0a 100755 --- a/plugins/mod_jk/mod_jk +++ b/plugins/mod_jk/mod_jk @@ -26,7 +26,7 @@ then # Status graph echo "multigraph mod_jk_status" echo "graph_title mod_jk Status" - echo "graph_category tomcat" + echo "graph_category webserver" echo "graph_info This graph shows the status of the different mod_jk nodes." echo "graph_vlabel Status" @@ -43,7 +43,7 @@ then do echo "multigraph mod_jk_requests_$i" echo "graph_title mod_jk Requests $i" - echo "graph_category tomcat" + echo "graph_category webserver" echo "graph_info This graph shows the status of requests to the mod_jk node: $i" echo "graph_vlabel Request" echo "busy.label Current" @@ -59,7 +59,7 @@ then do echo "multigraph mod_jk_traffic_$i" echo "graph_title mod_jk Traffic $i" - echo "graph_category tomcat" + echo "graph_category webserver" echo "graph_info This graph shows the traffic statistics to the mod_jk node: $i" echo "graph_vlabel Bytes/min" echo "transferred.label Write to node (bytes/min)" diff --git a/plugins/mogilefs/mogilefsd_activity b/plugins/mogilefs/mogilefsd_activity index 217b3f9c..a53edce6 100755 --- a/plugins/mogilefs/mogilefsd_activity +++ b/plugins/mogilefs/mogilefsd_activity @@ -126,7 +126,7 @@ if($ARGV[0] and $ARGV[0] eq "config") { print "graph_title MogileFS tracker activity\n"; print "graph_vlabel processes\n"; print "graph_args -l 0\n"; - print "graph_category MogileFS\n"; + print "graph_category fs\n"; print "bored_queryworkers.label bored_queryworkers\n"; print "bored_queryworkers.draw AREA\n"; print "processing_queries.label processing_queries\n"; diff --git a/plugins/mogilefs/mogilefsd_queries b/plugins/mogilefs/mogilefsd_queries index 6186e74a..ac433ac0 100755 --- a/plugins/mogilefs/mogilefsd_queries +++ b/plugins/mogilefs/mogilefsd_queries @@ -126,7 +126,7 @@ if($ARGV[0] and $ARGV[0] eq "config") { print "graph_title MogileFS tracker queries\n"; print "graph_vlabel queries/sec\n"; print "graph_args -l 0\n"; - print "graph_category MogileFS\n"; + print "graph_category fs\n"; print "queries.label queries\n"; print "queries.type DERIVE\n"; print "queries.draw AREA\n"; diff --git a/plugins/wiki/moinoin_pages b/plugins/moinmoin/moinoin_pages similarity index 100% rename from plugins/wiki/moinoin_pages rename to plugins/moinmoin/moinoin_pages diff --git a/plugins/mongodb/mongo_btree b/plugins/mongodb/mongo_btree index 820b2f04..bddd972a 100755 --- a/plugins/mongodb/mongo_btree +++ b/plugins/mongodb/mongo_btree @@ -31,7 +31,7 @@ def doConfig(): print "graph_title MongoDB btree stats" print "graph_args --base 1000 -l 0" print "graph_vlabel mb ${graph_period}" - print "graph_category MongoDB" + print "graph_category db" for k in get(): print k + ".label " + k diff --git a/plugins/mongodb/mongo_collection_ b/plugins/mongodb/mongo_collection_ old mode 100644 new mode 100755 index baea31ae..229435fe --- a/plugins/mongodb/mongo_collection_ +++ b/plugins/mongodb/mongo_collection_ @@ -1,4 +1,4 @@ -#!/usr/bin/env /usr/local/bin/python2 +#!/usr/bin/env python # -*- coding: utf-8 -*- # vim: set sts=4 sw=4 encoding=utf-8 @@ -33,11 +33,13 @@ #%# capabilities=suggest autoconf -from pymongo import Connection +import pymongo from operator import itemgetter settings_host = '127.0.0.1' settings_port = 27017 +# mongodb_uri will override host and port +settings_mongodb_uri = '' settings_db = 'mydb' settings_user = '' settings_password = '' @@ -50,6 +52,7 @@ typeIndex['collcount']['title'] = 'per collection document count' typeIndex['collcount']['yaxis'] = 'documents' typeIndex['collcount']['base'] = '1000' typeIndex['collcount']['scale'] = '--logarithmic -l1' +typeIndex['collcount']['category'] = 'db' typeIndex['collsize'] = {} typeIndex['collsize']['index'] = 'size' @@ -57,6 +60,7 @@ typeIndex['collsize']['title'] = 'per collection data size' typeIndex['collsize']['yaxis'] = 'Byte' typeIndex['collsize']['base'] = '1024' typeIndex['collsize']['scale'] = '--logarithmic -l1 --units=si' +typeIndex['collsize']['category'] = 'db' typeIndex['avgsize'] = {} typeIndex['avgsize']['index'] = 'avgObjSize' @@ -64,6 +68,7 @@ typeIndex['avgsize']['title'] = 'average object size' typeIndex['avgsize']['yaxis'] = 'Byte' typeIndex['avgsize']['base'] = '1024' typeIndex['avgsize']['scale'] = '--logarithmic --units=si' +typeIndex['avgsize']['category'] = 'db' typeIndex['storage'] = {} typeIndex['storage']['index'] = 'storageSize' @@ -71,6 +76,7 @@ typeIndex['storage']['title'] = 'per collection storage size' typeIndex['storage']['yaxis'] = 'Byte' typeIndex['storage']['base'] = '1024' typeIndex['storage']['scale'] = '--logarithmic -l1 --units=si' +typeIndex['storage']['category'] = 'db' typeIndex['indexsize'] = {} typeIndex['indexsize']['index'] = 'totalIndexSize' @@ -78,11 +84,14 @@ typeIndex['indexsize']['title'] = 'per collection index size' typeIndex['indexsize']['yaxis'] = 'Byte' typeIndex['indexsize']['base'] = '1024' typeIndex['indexsize']['scale'] = '--logarithmic -l 1 --units=si' - +typeIndex['indexsize']['category'] = 'db' def getCollstats(graphtype): - con = Connection(settings_host, int(settings_port), slave_okay=True) + if settings_mongodb_uri: + con = pymongo.MongoClient(settings_mongodb_uri) + else: + con = pymongo.MongoClient(settings_host, int(settings_port)) if settings_user: db = con['admin'] @@ -106,7 +115,7 @@ def getCollstats(graphtype): stats_tmp[collname]['value'] += long(stats[typeIndex[graphtype]['index']]) - con.disconnect() + con.close() for collname, item in sorted(stats_tmp.items()): yield ("%s" % collname, item['value'], item['dbname']) @@ -132,7 +141,7 @@ def doConfig(base,graphtype): print "graph_title MongoDB " + typeIndex[graphtype]['title'] + " for database " + d print "graph_args --base " + typeIndex[graphtype]['base'] + " " + typeIndex[graphtype]['scale'] print "graph_vlabel " + typeIndex[graphtype]['yaxis'] - print "graph_category MongoDB" + print "graph_category db" print "%s_%s.label %s" % (graphtype, k, k) print "%s_%s.min 0" % (graphtype, k) print "%s_%s.draw LINE1" % (graphtype, k) diff --git a/plugins/mongodb/mongo_conn b/plugins/mongodb/mongo_conn index b149f78f..62324c65 100755 --- a/plugins/mongodb/mongo_conn +++ b/plugins/mongodb/mongo_conn @@ -26,7 +26,7 @@ def doConfig(): print "graph_title MongoDB current connections" print "graph_args --base 1000 -l 0" print "graph_vlabel connections" - print "graph_category MongoDB" + print "graph_category db" print name + ".label " + name diff --git a/plugins/mongodb/mongo_lag b/plugins/mongodb/mongo_lag index b70837fc..80fdfbd2 100755 --- a/plugins/mongodb/mongo_lag +++ b/plugins/mongodb/mongo_lag @@ -25,7 +25,7 @@ import pymongo def _get_members(): host = os.environ.get('host', '127.0.0.1') port = os.environ.get('port', 27017) - conn = pymongo.Connection(host,port) + conn = pymongo.MongoClient(host,port) repl_status = conn.admin.command("replSetGetStatus") members = {} @@ -49,7 +49,7 @@ def config(): print """graph_title MongoDB replication lag graph_args --base 1000 graph_vlabel Replication lag (seconds) -graph_category MongoDB +graph_category db """ for member in _get_members(): diff --git a/plugins/mongodb/mongo_lock b/plugins/mongodb/mongo_lock index 4b94aeca..ef490d7d 100755 --- a/plugins/mongodb/mongo_lock +++ b/plugins/mongodb/mongo_lock @@ -20,7 +20,10 @@ name = "locked" def doData(): status = getServerStatus() if status["version"] >= "2.2.0": - ratio = float(status["globalLock"]["lockTime"]) / status["globalLock"]["totalTime"] + if status["globalLock"]["lockTime"]["$numberLong"]: + ratio = float(status["globalLock"]["lockTime"]["$numberLong"]) / float(status["globalLock"]["totalTime"]["$numberLong"]) + else: + ratio = float(status["globalLock"]["lockTime"]) / status["globalLock"]["totalTime"] else: ratio = status["globalLock"]["ratio"] print name + ".value " + str( 100 * ratio ) @@ -30,7 +33,7 @@ def doConfig(): print "graph_title MongoDB write lock percentage" print "graph_args --base 1000 -l 0 " print "graph_vlabel percentage" - print "graph_category MongoDB" + print "graph_category db" print name + ".label " + name diff --git a/plugins/mongodb/mongo_mem b/plugins/mongodb/mongo_mem index 3a4d96b5..8f720602 100755 --- a/plugins/mongodb/mongo_mem +++ b/plugins/mongodb/mongo_mem @@ -27,7 +27,7 @@ def doConfig(): print "graph_title MongoDB memory usage" print "graph_args --base 1024 -l 0 --vertical-label Bytes" - print "graph_category MongoDB" + print "graph_category db" for k in getServerStatus()["mem"]: if ok( k ): diff --git a/plugins/mongodb/mongo_ops b/plugins/mongodb/mongo_ops index 2bb9c973..15f6b8b9 100755 --- a/plugins/mongodb/mongo_ops +++ b/plugins/mongodb/mongo_ops @@ -26,7 +26,7 @@ def doConfig(): print "graph_title MongoDB ops" print "graph_args --base 1000 -l 0" print "graph_vlabel ops / ${graph_period}" - print "graph_category MongoDB" + print "graph_category db" print "graph_total total" for k in getServerStatus()["opcounters"]: diff --git a/plugins/monit/monit_parser b/plugins/monit/monit_parser index f89bd4db..f7fb73a0 100755 --- a/plugins/monit/monit_parser +++ b/plugins/monit/monit_parser @@ -1,63 +1,126 @@ -#!/usr/bin/python -# Monit status parser plugin for munin +#!/usr/bin/python3 -# This is very raw, but it should work for you out of the box. You of course -# need to have monit configured such that the 'monit status' command works. +""" +=head1 NAME -# Todd Troxell +monit_parser - Monit status parser plugin for munin. -STATUS_CMD = "monit status" +=head1 APPLICABLE SYSTEMS + +Any system being able to query monit servers via http. + +Monit needs to be configured with the httpd port enabled, e.g.: + + set httpd port 2812 + +Optionally monit authentication can be configured, e.g.: + + set httpd port 2812 + allow munin:s3cr3t read-only + + +=head1 CONFIGURATION + +By default the monit instance at localhost is queried: + + [monit_parser] + env.port 2812 + env.host localhost + +Optionally monit authentication can be configured, e.g.: + + [monit_parser] + env.username munin + env.password s3cr3t + + +=head1 AUTHOR + + Todd Troxell + Lars Kruse + + +=head1 MAGIC MARKERS + + family=auto + capabilities=autoconf + +=cut +""" + +import xml.dom.minidom +import os import sys -import re -from commands import getstatusoutput +import urllib.request +import base64 + +MONIT_XML_URL = ("http://{host}:{port}/_status?format=xml" + .format(host=os.getenv("host", "localhost"), + port=os.getenv("port", "2812"))) + def sanitize(s): - OK_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789" - out = str() - for char in s: - if char.lower() in OK_CHARS: - out += char - return out + ok_chars = "abcdefghijklmnopqrstuvwxyz0123456789" + return "".join([char for char in s if char in ok_chars]) -procs = dict() -status, output = getstatusoutput(STATUS_CMD) -if status != 0: - print "Errror running status command: %s" % (output) - sys.exit(status) +def get_monit_status_xml(): + try: + req = urllib.request.Request(url=MONIT_XML_URL) + if os.getenv("password"): + auth_str = "%s:%s" % (os.getenv("username"), os.getenv("password")) + auth_bytes = bytes(auth_str, "utf-8") + auth_base64_bytes = base64.b64encode(auth_bytes) + auth_base64_str = str(auth_base64_bytes, "ascii") + auth_value = "Basic %s" % auth_base64_str + req.add_header("Authorization", auth_value) + conn = urllib.request.urlopen(req) + except urllib.error.URLError as exc: + conn = None + if conn is None: + raise RuntimeError("Failed to open monit status URL: {}".format(MONIT_XML_URL)) + else: + return xml.dom.minidom.parse(conn) -output = output.split('\n') -cur_proc = None -for line in output: - m = re.match("^Process '(.*)'.*$", line) - if m: - cur_proc = sanitize(m.group(1)) - try: - procs[cur_proc] - except KeyError: - procs[cur_proc] = dict() - continue - m = re.match(" memory kilobytes total\s+([0-9]+).*$", line) - if m: - procs[cur_proc]["total_memory"] = m.group(1) - continue - m = re.match(" cpu percent total\s+([.0-9]+)%.*$", line) - if m: - procs[cur_proc]["total_cpu"] = m.group(1) - continue - -if len(sys.argv) > 1 and sys.argv[1] == 'config': - print 'graph_title Per process stats from Monit' - print 'graph_vlabel numbers' - print 'graph_category monit' + +def parse_processes(): + dom = get_monit_status_xml() + procs = {} + for item in dom.getElementsByTagName("service"): + if item.getAttribute("type") == "3": + # daemon with memory usage and CPU + name = item.getElementsByTagName("name")[0].childNodes[0].data + memory_usage = item.getElementsByTagName("memory")[0].getElementsByTagName( + "kilobytetotal")[0].childNodes[0].data + cpu_usage = item.getElementsByTagName("cpu")[0].getElementsByTagName( + "percenttotal")[0].childNodes[0].data + procs[name] = {} + procs[name]["total_memory"] = memory_usage + procs[name]["total_cpu"] = cpu_usage + return procs + + +action = sys.argv[1] if (len(sys.argv) > 1) else None + +if action == 'autoconf': + try: + get_monit_status_xml() + print("yes") + except RuntimeError: + print("no (failed to request monit status)") +elif action == 'config': + procs = parse_processes() + print('graph_title Per process stats from Monit') + print('graph_vlabel usage of memory [kB] or cpu [%]') + print('graph_category munin') for process in procs: for stat in procs[process]: - print "monit_%s_%s.label %s.%s" % (process, stat, process, stat) + print("monit_%s_%s.label %s.%s" % (sanitize(process), stat, process, stat)) if stat == 'total_memory': - print "monit_%s_%s.warning 1:" % (process, stat) - sys.exit(0) - -for process in procs: - for stat in procs[process]: - print "monit_%s_%s.value %s" % (process, stat, procs[process][stat]) + # the allocated memory may never be zero + print("monit_%s_%s.warning 1:" % (sanitize(process), stat)) +else: + for process, stats in parse_processes().items(): + for stat_key, stat_value in stats.items(): + print("monit_%s_%s.value %s" % (sanitize(process), stat_key, stat_value)) diff --git a/plugins/moodle/moodle_files.php b/plugins/moodle/moodle_files old mode 100644 new mode 100755 similarity index 96% rename from plugins/moodle/moodle_files.php rename to plugins/moodle/moodle_files index 13a9a31e..f66eee5b --- a/plugins/moodle/moodle_files.php +++ b/plugins/moodle/moodle_files @@ -64,7 +64,7 @@ if (count($argv) === 2 && $argv[1] === 'config') { echo "graph_title Moodle Files Number\n"; echo "graph_args --base 1000 --lower-limit 0\n"; echo "graph_vlabel files\n"; - echo "graph_category Moodle\n"; + echo "graph_category cms\n"; echo "graph_scale no\n"; echo "graph_total total\n"; echo "graph_info Displays the total number of moodle users files and repartition by type\n"; @@ -80,7 +80,7 @@ if (count($argv) === 2 && $argv[1] === 'config') { echo "graph_title Moodle Files Size\n"; echo "graph_args --base 1024 --lower-limit 0\n"; echo "graph_vlabel size\n"; - echo "graph_category Moodle\n"; + echo "graph_category cms\n"; echo "graph_total total\n"; echo "graph_info Displays the total size of moodle users files and repartition by type\n"; @@ -98,7 +98,7 @@ echo "multigraph file_number\n"; echo "graph_title Moodle Files Number\n"; echo "graph_args --base 1000 --lower-limit 0\n"; echo "graph_vlabel files\n"; -echo "graph_category Moodle\n"; +echo "graph_category cms\n"; echo "graph_scale no\n"; echo "graph_total total\n"; echo "graph_info Displays the total number of moodle users files and repartition by type\n"; @@ -117,7 +117,7 @@ echo "multigraph file_size\n"; echo "graph_title Moodle Files Size\n"; echo "graph_args --base 1024 --lower-limit 0\n"; echo "graph_vlabel size\n"; -echo "graph_category Moodle\n"; +echo "graph_category cms\n"; echo "graph_total total\n"; echo "graph_info Displays the total size of moodle users files and repartition by type\n"; diff --git a/plugins/moodle/moodle_logs.php b/plugins/moodle/moodle_logs old mode 100644 new mode 100755 similarity index 97% rename from plugins/moodle/moodle_logs.php rename to plugins/moodle/moodle_logs index f6e0a2e8..79025d6b --- a/plugins/moodle/moodle_logs.php +++ b/plugins/moodle/moodle_logs @@ -41,7 +41,7 @@ if (count($argv) === 2 && $argv[1] === 'config') { echo "graph_title Moodle Logs\n"; echo "graph_args --base 1000 --lower-limit 0\n"; echo "graph_vlabel logs\n"; - echo "graph_category Moodle\n"; + echo "graph_category cms\n"; echo "graph_scale no\n"; echo "graph_info Displays the number of new logs written\n"; echo "logs.label logs\n"; diff --git a/plugins/moodle/modules/moodle_mod_chat.php b/plugins/moodle/moodle_mod_chat old mode 100644 new mode 100755 similarity index 98% rename from plugins/moodle/modules/moodle_mod_chat.php rename to plugins/moodle/moodle_mod_chat index 7db3fd8f..6448db3d --- a/plugins/moodle/modules/moodle_mod_chat.php +++ b/plugins/moodle/moodle_mod_chat @@ -38,7 +38,7 @@ if (count($argv) === 2 && $argv[1] === 'config') { echo "graph_title Moodle Chat Users\n"; echo "graph_args --base 1000 --lower-limit 0\n"; echo "graph_vlabel users\n"; - echo "graph_category Moodle\n"; + echo "graph_category cms\n"; echo "graph_scale no\n"; echo "graph_info Displays the number of users connected and posting message in chat sessions\n"; echo "chat_users_connected.label users connected\n"; diff --git a/plugins/moodle/modules/moodle_mod_forum.php b/plugins/moodle/moodle_mod_forum old mode 100644 new mode 100755 similarity index 97% rename from plugins/moodle/modules/moodle_mod_forum.php rename to plugins/moodle/moodle_mod_forum index 363a6be8..896d0c59 --- a/plugins/moodle/modules/moodle_mod_forum.php +++ b/plugins/moodle/moodle_mod_forum @@ -39,7 +39,7 @@ if (count($argv) === 2 && $argv[1] === 'config') { echo "graph_title Moodle Forum Posts\n"; echo "graph_args --base 1000 --lower-limit 0\n"; echo "graph_vlabel number\n"; - echo "graph_category Moodle\n"; + echo "graph_category cms\n"; echo "graph_scale no\n"; echo "graph_info Displays the sum of new forums posts / discussions in your Moodle site\n"; echo "forum_posts.label posts\n"; diff --git a/plugins/moodle/modules/moodle_mod_quiz.php b/plugins/moodle/moodle_mod_quiz old mode 100644 new mode 100755 similarity index 97% rename from plugins/moodle/modules/moodle_mod_quiz.php rename to plugins/moodle/moodle_mod_quiz index 6e270744..e3d73cd7 --- a/plugins/moodle/modules/moodle_mod_quiz.php +++ b/plugins/moodle/moodle_mod_quiz @@ -39,7 +39,7 @@ if (count($argv) === 2 && $argv[1] === 'config') { echo "graph_title Moodle Quiz Attempts\n"; echo "graph_args --base 1000 --lower-limit 0\n"; echo "graph_vlabel attempts\n"; - echo "graph_category Moodle\n"; + echo "graph_category cms\n"; echo "graph_scale no\n"; echo "graph_info Displays the sum quiz attempts in your Moodle site\n"; echo "quiz_attempts.label attempts\n"; diff --git a/plugins/moodle/moodle_modules_total.php b/plugins/moodle/moodle_modules_total old mode 100644 new mode 100755 similarity index 98% rename from plugins/moodle/moodle_modules_total.php rename to plugins/moodle/moodle_modules_total index 3f054494..94087b58 --- a/plugins/moodle/moodle_modules_total.php +++ b/plugins/moodle/moodle_modules_total @@ -51,7 +51,7 @@ if (count($argv) === 2 && $argv[1] === 'config') { echo "graph_title Moodle Modules\n"; echo "graph_args --base 1000 --lower-limit 0\n"; echo "graph_vlabel modules\n"; - echo "graph_category Moodle\n"; + echo "graph_category cms\n"; echo "graph_scale no\n"; echo "graph_info Displays the sum of module, as well as module instance number by type, in your Moodle site\n"; echo "graph_total total\n"; diff --git a/plugins/moodle/moodle_users_online.php b/plugins/moodle/moodle_users_online old mode 100644 new mode 100755 similarity index 97% rename from plugins/moodle/moodle_users_online.php rename to plugins/moodle/moodle_users_online index bac9ff1f..9a9e4467 --- a/plugins/moodle/moodle_users_online.php +++ b/plugins/moodle/moodle_users_online @@ -39,7 +39,7 @@ if (count($argv) === 2 && $argv[1] === 'config') { echo "graph_title Moodle Online Users\n"; echo "graph_args --base 1000 --lower-limit 0\n"; echo "graph_vlabel users\n"; - echo "graph_category Moodle\n"; + echo "graph_category cms\n"; echo "graph_scale no\n"; echo "graph_info Displays the sum of online users in your Moodle site\n"; echo "users_online.label online users\n"; diff --git a/plugins/moodle/moodle_users_total.php b/plugins/moodle/moodle_users_total old mode 100644 new mode 100755 similarity index 98% rename from plugins/moodle/moodle_users_total.php rename to plugins/moodle/moodle_users_total index 166036f3..bc37f8e8 --- a/plugins/moodle/moodle_users_total.php +++ b/plugins/moodle/moodle_users_total @@ -37,7 +37,7 @@ if (count($argv) === 2 && $argv[1] === 'config') { echo "graph_title Moodle Total Users\n"; echo "graph_args --base 1000 --lower-limit 0\n"; echo "graph_vlabel users\n"; - echo "graph_category Moodle\n"; + echo "graph_category cms\n"; echo "graph_scale no\n"; echo "graph_info Displays the sum of users, as well as active, suspended and deleted accounts, in your Moodle site\n"; echo "graph_total total\n"; diff --git a/plugins/mpd/mpdstats_songs-day.png b/plugins/mpd/example-graphs/mpdstats_-day.png similarity index 100% rename from plugins/mpd/mpdstats_songs-day.png rename to plugins/mpd/example-graphs/mpdstats_-day.png diff --git a/plugins/mpd/mpdstats_ b/plugins/mpd/mpdstats_ index ec78ad4e..f9d0360a 100755 --- a/plugins/mpd/mpdstats_ +++ b/plugins/mpd/mpdstats_ @@ -19,8 +19,10 @@ work like a charm already. =head1 INSTALLATION -This wildcard plugin should be symlinked using one of these names: -mpdstats_artists, mpdstats_albums, mpdstats_songs +This wildcard plugin can be symlinked using one of the statistic +categories of mpd. See the output of "echo stats | nc MPD_HOST 6600". +Without a symlink configuration (no suffix after the underscore) all +stats are shown. =head1 CONFIGURATION @@ -66,32 +68,25 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. =cut +. "$MUNIN_LIBDIR/plugins/plugin.sh" MPDHOST=${mpd_host:-localhost} MPDPORT=${mpd_port:-6600} -NCBIN=${netcat:-/bin/nc} +NCBIN=${netcat:-$(which nc)} + +ACTION="$(basename "$0" | sed 's/^.*_//')" # # FUNCTIONS # do_autoconf () { - case "$ACTION" in - albums|artists|songs) - ;; - *) - echo "no (not a valid symlink name, please read the plugin doc)" - exit 1 - ;; - esac - - if [ ! -x $NCBIN ] ; then - echo "no ($NCBIN: not found or not executable)" + if [ -z "$NCBIN" ] ; then + echo "no (missing netcat program ('nc'))" exit 1 fi - echo version | $NCBIN -q 2 $MPDHOST $MPDPORT 1>/dev/null 2>&1 - if [ "$?" != 0 ] ; then + if ! echo version | "$NCBIN" "$MPDHOST" "$MPDPORT" >/dev/null 2>&1; then echo "no (connection failed)" exit 1 fi @@ -100,35 +95,65 @@ do_autoconf () { exit 0 } + +get_mpd_stats_keys() { + echo stats | "$NCBIN" "$MPDHOST" "$MPDPORT" | awk 'BEGIN {FS=":"} /^[^ ]+:/ {print $1}' +} + + +print_mpd_stat_value() { + local key="$1" + local fieldname + local stat_value + fieldname="$(clean_fieldname "$key")" + stat_value="$(echo stats | "$NCBIN" "$MPDHOST" "$MPDPORT" | awk "/^${key}:/ {print \$2}")" + echo "${fieldname}.value $stat_value" +} + + # # MAIN # -SCRIPTNAME=${0##*/} -ACTION=${SCRIPTNAME##*_} case "$1" in autoconf) do_autoconf ;; suggest) - echo "$ACTION" - exit 0 + get_mpd_stats_keys ;; config) - echo "graph_category MPD -graph_title $ACTION in the database -graph_info Number of $ACTION in the database. -graph_vlabel $ACTION in the db -graph_args --base 1000 -l 0 -graph_scale no -$ACTION.type GAUGE -$ACTION.draw LINE2 -$ACTION.label $ACTION" - exit 0 + echo "graph_category streaming" + echo "graph_args --base 1000 -l 0" + echo "graph_scale no" + if [ -z "$ACTION" ]; then + echo "graph_title MPD Statistics" + echo "graph_vlabel number of items" + for stat_key in $(get_mpd_stats_keys); do + fieldname="$(clean_fieldname "$stat_key")" + echo "${fieldname}.type GAUGE" + echo "${fieldname}.draw LINE" + echo "${fieldname}.label $stat_key" + done + else + # Show only a single stat + echo "graph_title $ACTION in the MPD database" + echo "graph_info Number of $ACTION in the database." + echo "graph_vlabel $ACTION in the db" + echo "$ACTION.type GAUGE" + echo "$ACTION.draw LINE2" + echo "$ACTION.label $ACTION" + fi ;; *) - printf "$ACTION.value " - echo stats | $NCBIN -q 2 $MPDHOST $MPDPORT | awk " /^${ACTION}/ {print \$2}" + if [ -z "$ACTION" ]; then + for stat_key in $(get_mpd_stats_keys); do + print_mpd_stat_value "$stat_key" + done + else + print_mpd_stat_value "$ACTION" + fi ;; esac +exit 0 diff --git a/plugins/mssql/microsoft-sql b/plugins/mssql/microsoft-sql index f29e6a27..457298b1 100755 --- a/plugins/mssql/microsoft-sql +++ b/plugins/mssql/microsoft-sql @@ -93,7 +93,7 @@ if ARGV[0] == "autoconf" elsif ARGV[0] == "config" puts "graph_args --base 1000 -r --lower-limit 0" puts "graph_title MSSQL Transactions/s" - puts "graph_category MSSQL" + puts "graph_category db" puts "graph_info This graph shows transactions/s" puts "graph_vlabel transactions/s" puts "graph_scale no" diff --git a/plugins/mssql/microsoft-sql-buffer-cache-hit-ratio b/plugins/mssql/microsoft-sql-buffer-cache-hit-ratio index d5f23c97..ef48f336 100755 --- a/plugins/mssql/microsoft-sql-buffer-cache-hit-ratio +++ b/plugins/mssql/microsoft-sql-buffer-cache-hit-ratio @@ -83,7 +83,7 @@ if ARGV[0] == "autoconf" elsif ARGV[0] == "config" puts "graph_args --base 1000 -r --lower-limit 0" puts "graph_title MSSQL Buffer Cache Hit Ratio " - puts "graph_category MSSQL" + puts "graph_category db" puts "graph_info This graph shows Buffer Cache Hit Ratio" puts "graph_vlabel %" puts "graph_scale no" diff --git a/plugins/mssql/microsoft-sql-data-file-sizes b/plugins/mssql/microsoft-sql-data-file-sizes index 62fb0119..1e068e5d 100755 --- a/plugins/mssql/microsoft-sql-data-file-sizes +++ b/plugins/mssql/microsoft-sql-data-file-sizes @@ -91,7 +91,7 @@ if ARGV[0] == "autoconf" elsif ARGV[0] == "config" puts "graph_args --base 1024k -r --lower-limit 0" puts "graph_title MSSQL DB File Sizes" - puts "graph_category MSSQL" + puts "graph_category db" puts "graph_info This graph shows DB File Sizes (MB)" puts "graph_vlabel MB" puts "graph_scale no" diff --git a/plugins/mssql/microsoft-sql-log-file-size b/plugins/mssql/microsoft-sql-log-file-size index cbec4af4..45951c6a 100755 --- a/plugins/mssql/microsoft-sql-log-file-size +++ b/plugins/mssql/microsoft-sql-log-file-size @@ -91,7 +91,7 @@ if ARGV[0] == "autoconf" elsif ARGV[0] == "config" puts "graph_args --base 1024k -r --lower-limit 0" puts "graph_title MSSQL DB Log File Sizes" - puts "graph_category MSSQL" + puts "graph_category db" puts "graph_info This graph shows DB Log File Sizes (MB)" puts "graph_vlabel MB" puts "graph_scale no" diff --git a/plugins/other/mumble_users b/plugins/mumble/mumble_users similarity index 97% rename from plugins/other/mumble_users rename to plugins/mumble/mumble_users index 71ea9e2f..f69edb24 100755 --- a/plugins/other/mumble_users +++ b/plugins/mumble/mumble_users @@ -26,6 +26,7 @@ murmur = Murmur.MetaPrx.checkedCast(prx) if (sys.argv.__len__() == 2) and (sys.argv[1] == "config"): print "graph_title Mumble users" + print "graph_category voip" print "graph_vlabel users" print "graph_args --lower-limit 0" for server in murmur.getAllServers(): diff --git a/plugins/other/murmur-stats b/plugins/mumble/murmur-stats similarity index 98% rename from plugins/other/murmur-stats rename to plugins/mumble/murmur-stats index de835f22..bb87951d 100755 --- a/plugins/other/murmur-stats +++ b/plugins/mumble/murmur-stats @@ -26,6 +26,7 @@ import Murmur if (sys.argv[1:]): if (sys.argv[1] == "config"): print 'graph_title Murmur (Port %s)' % (serverport) + print 'graph_category voip' print 'graph_vlabel Count' print 'users.label Users' print 'uptime.label Uptime in days' diff --git a/plugins/munin/munin_events b/plugins/munin/munin_events new file mode 100755 index 00000000..2a355bd9 --- /dev/null +++ b/plugins/munin/munin_events @@ -0,0 +1,142 @@ +#!/bin/sh +# -*- sh -*- +: <<=cut + +=head1 NAME + +munin_events - Plugin to monitor munin updates + +=head1 APPLICABLE SYSTEMS + +All systems with "bash", "logtail" and "munin" + +=head1 CONFIGURATION + +The following is the default configuration + + [munin_events] + user munin + env.muninupdate /var/log/munin/munin-update.log + env.logtail2 /usr/sbin/logtail2 + +You could trigger alerts on update failures + + [munin_events] + env.munin_fatal_critical 0 + env.munin_error_critical 0 + env.munin_warning_warning 0 + env.munin_warning_critical 5 + +=head1 INTERPRETATION + +This plugin shows a graph with one line per munin state: +INFO, WARNING, ERROR, FATAL. + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=head1 VERSION + + 1.2.20160514 + +=head1 AUTHOR + +Viktor Szépe + +=head1 LICENSE + +GPLv2 + +=cut + + +############################## +# Includes + +# shellcheck disable=SC1090 +. "$MUNIN_LIBDIR/plugins/plugin.sh" + +############################## +# Configurable variables +muninupdate=${muninupdate:-/var/log/munin/munin-update.log} +logtail_bin=${logtail_bin:-/usr/sbin/logtail2} + +############################## +# Functions + +# Print one value +do_value() { + FIELD="$1" + EVENT_LABEL="$2" + + EVENT_COUNT="$("$logtail_bin" -t "$muninupdate" 2> /dev/null | grep -c "^[0-9/: ]\{19\} \[${EVENT_LABEL}\]")" + if echo "$EVENT_COUNT" | grep -q "[^0-9]"; then + echo "Cannot determine event count" 1>&2 + exit 10 + fi + + echo "${FIELD}.value ${EVENT_COUNT}" +} + +# Print the munin values +values() { + do_value 'munin_info' 'INFO' + do_value 'munin_warning' 'WARNING' + do_value 'munin_error' 'ERROR' + do_value 'munin_fatal' 'FATAL' + # Set offset + "$logtail_bin" "$muninupdate" > /dev/null 1>&2 + chmod 640 "${muninupdate}.offset" +} + +# Print the munin config +config() { + echo 'graph_title Munin update events groupped by log levels' + echo 'graph_info This graph shows INFO, WARNING, ERROR and FATAL events' + echo 'graph_category munin' + echo 'graph_vlabel Number of events' + + echo 'graph_args --base 1000 -l 0' + echo 'graph_total total' + echo 'graph_printf %6.0lf' + + echo 'munin_info.label INFO' + print_warning munin_info + print_critical munin_info + echo 'munin_warning.label WARNING' + print_warning munin_warning + print_critical munin_warning + echo 'munin_error.label ERROR' + print_warning munin_error + print_critical munin_error + echo 'munin_fatal.label FATAL' + print_warning munin_fatal + print_critical munin_fatal +} + +# Print autoconfiguration hint +autoconf() { + if [ -r "${muninupdate}" ] && [ -x "$logtail_bin" ]; then + echo "yes" + else + echo "missing (${muninupdate} or (${logtail_bin})" + fi + exit +} + +############################## +# Main + +case "$1" in + config) + config + ;; + autoconf) + autoconf + ;; + *) + values + ;; +esac diff --git a/plugins/mysql/hs_read b/plugins/mysql/hs_read old mode 100644 new mode 100755 index 2468f056..ae3e093e --- a/plugins/mysql/hs_read +++ b/plugins/mysql/hs_read @@ -41,7 +41,7 @@ if [ "$1" = "config" ]; then echo 'graph_title HS Read port connections' echo "graph_args --base 1000 -l 0" - echo 'graph_category mysql' + echo 'graph_category db' echo 'total.label Total' echo 'total.draw AREA' echo 'total.min 0' diff --git a/plugins/mysql/hs_write b/plugins/mysql/hs_write old mode 100644 new mode 100755 index 5442a4d4..6ab14b6f --- a/plugins/mysql/hs_write +++ b/plugins/mysql/hs_write @@ -41,7 +41,7 @@ if [ "$1" = "config" ]; then echo 'graph_title HS Write port connections' echo "graph_args --base 1000 -l 0" - echo 'graph_category mysql' + echo 'graph_category db' echo 'total.label Total' echo 'total.draw AREA' echo 'total.min 0' diff --git a/plugins/mysql/mysql-schema-size b/plugins/mysql/mysql-schema-size index f25c9872..d1227479 100755 --- a/plugins/mysql/mysql-schema-size +++ b/plugins/mysql/mysql-schema-size @@ -28,7 +28,7 @@ while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { if($argc > 1 && $argv[1] == 'config') { $text = << 1 && $argv[1] == 'config') { $text = << $counts{$b} } keys %counts) { last if $i++ >= $numusers; + if ($user eq "system user") { + #skip internal user that manages binlog operations for slave servers. + next; + } $total += $counts{$user}; $print_user = $user; if($print_user eq "root") { @@ -111,7 +115,7 @@ graph_title MySQL Connections per user graph_args --base 1000 --lower-limit 0 graph_vlabel Connections graph_info The number of current connexions per user. -graph_category mysql +graph_category db graph_total Total EOM @@ -125,6 +129,10 @@ EOM my $i = 0; foreach my $user (reverse sort { $counts{$a} <=> $counts{$b} } keys %counts) { last if $i++ >= $numusers; + if ($user eq "system user") { + #skip internal user that manages binlog operations for slave servers. + next; + } my $print_user = $user; if($print_user eq "root") { $print_user = "root_"; diff --git a/plugins/mysql/mysql_qcache b/plugins/mysql/mysql_qcache index b0744369..605adf9b 100755 --- a/plugins/mysql/mysql_qcache +++ b/plugins/mysql/mysql_qcache @@ -79,7 +79,7 @@ sub print_config { print('graph_title MySQL Queries in cache graph_args --base 1000 graph_vlabel queries -graph_category mysql +graph_category db graph_info Plugin available at http://rodolphe.quiedeville.org/hack/munin/ '); diff --git a/plugins/mysql/mysql_qcache_mem b/plugins/mysql/mysql_qcache_mem index 0fe06c31..cd5ec1d2 100755 --- a/plugins/mysql/mysql_qcache_mem +++ b/plugins/mysql/mysql_qcache_mem @@ -92,7 +92,7 @@ sub print_config { print('graph_title MySQL Queries Cache Size graph_args --base 1024 -l 0 graph_vlabel bytes -graph_category mysql +graph_category db graph_order used free graph_total Total graph_info Plugin available at http://rodolphe.quiedeville.org/hack/munin/ diff --git a/plugins/mysql/mysql_report b/plugins/mysql/mysql_report index 8ae0e02c..e8bf94fa 100755 --- a/plugins/mysql/mysql_report +++ b/plugins/mysql/mysql_report @@ -54,7 +54,6 @@ mysqlbin=$(which mysql) default_errorvalue=30 default_title="Results from MySQL queries" -default_category="mysql" default_vlabel="value / sec" default_info="This graph shows results of one or more SQL queries." default_args="--base 1000 -l 0" @@ -77,7 +76,6 @@ fi [ -n "${errorvalue}" ] || errorvalue=${default_errorvalue} [ -n "${title}" ] || title="${default_title}" -[ -n "${category}" ] || category="${default_category}" [ -n "${vlabel}" ] || vlabel="${default_vlabel}" [ -n "${info}" ] || info="${default_info}" [ -n "${args}" ] || args="${default_args}" @@ -89,7 +87,7 @@ graph_title ${title} graph_args ${args} graph_scale ${scale} graph_vlabel ${vlabel} -graph_category ${category} +graph_category db graph_info ${info} EOH1 [ -n "${period}" ] && echo "graph_period ${period}" diff --git a/plugins/mysql/mysql_size_ b/plugins/mysql/mysql_size_ index 1199fcf9..1b96107f 100755 --- a/plugins/mysql/mysql_size_ +++ b/plugins/mysql/mysql_size_ @@ -115,7 +115,7 @@ sub print_config { print("graph_title MySQL database $db size\n"); print ('graph_args --base 1024 -l 0 graph_vlabel bytes -graph_category mysql +graph_category db graph_info Plugin available at http://rodolphe.quiedeville.org/hack/munin/ '); diff --git a/plugins/mysql/mysql_size_all b/plugins/mysql/mysql_size_all index e22645d2..c6f617b9 100755 --- a/plugins/mysql/mysql_size_all +++ b/plugins/mysql/mysql_size_all @@ -126,7 +126,7 @@ sub print_config { print("graph_title MySQL databases size\n"); print ('graph_args --base 1024 -l 0 graph_vlabel bytes -graph_category mysql +graph_category db graph_info Plugin available at http://rodolphe.quiedeville.org/hack/munin/ '); diff --git a/plugins/mysql/mysql_size_ondisk b/plugins/mysql/mysql_size_ondisk index c18b9a38..1045b04a 100755 --- a/plugins/mysql/mysql_size_ondisk +++ b/plugins/mysql/mysql_size_ondisk @@ -50,7 +50,7 @@ then elif [ "${1:-}" = "config" ] then echo "graph_title MySQL on-disk database size" - echo "graph_category mysql" + echo "graph_category db" # graph_info cannot have newlines - replace by
which will be rendered to newlines in # the web interface. echo "graph_info ${INFO//$'\n'/
}" diff --git a/plugins/mysql/mysql_slave b/plugins/mysql/mysql_slave index e4cef7e3..600ade22 100755 --- a/plugins/mysql/mysql_slave +++ b/plugins/mysql/mysql_slave @@ -79,7 +79,7 @@ sub print_config { print "graph_title MySQL Slave Status\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Seconds\n"; - print "graph_category mysql\n"; + print "graph_category db\n"; print "seconds.label Seconds behind master\n"; print "seconds.min 0\n"; print "seconds.draw LINE2\n"; diff --git a/plugins/mysql/mysql_slave_threads b/plugins/mysql/mysql_slave_threads index abd9ac6b..f09f97af 100755 --- a/plugins/mysql/mysql_slave_threads +++ b/plugins/mysql/mysql_slave_threads @@ -28,7 +28,7 @@ if(defined $ARGV[0] && $ARGV[0] eq 'config') { print < This graph shows current services warning,critical,unknown,checked,scheduled,flapping,down svcchkext => This graph shows the service check execution times svcchklat => This graph shows the service check latency times - svcchksc => This graph shows the serivce check state change % + svcchksc => This graph shows the service check state change % hosts => I This graphs the current host problems. hostchkdetail => This graph shows current hosts down,unreachable,checked,scheduled,flapping,down @@ -127,7 +127,7 @@ $graphs{services} = { config => { args => '--lower-limit 0', vlabel => 'Service Problems', - category => 'nagios', + category => 'munin', title => 'Service Problems', info => 'Current Service Problems by Alert Status', }, @@ -144,7 +144,7 @@ $graphs{svcchkdetail} = { config => { args => '--lower-limit 0', vlabel => 'Total Number of Service Checks', - category => 'details', + category => 'munin', title => 'Detailed Service Info', info => 'Detailed Service Check Information', }, @@ -164,7 +164,7 @@ $graphs{svcchksc} = { config => { args => '--lower-limit 0 --upper-limit 100', vlabel => '%', - category => 'statechange', + category => 'munin', title => 'Service State Change', info => 'Total Percent of State Change between checks', }, @@ -180,7 +180,7 @@ $graphs{svcchklat} = { config => { args => '--lower-limit 0', vlabel => 'time (ms)', - category => 'latency', + category => 'munin', title => 'Service Check Latency Times', info => 'Service Check Latency Times', }, @@ -196,7 +196,7 @@ $graphs{svcchkext} = { config => { args => '--lower-limit 0', vlabel => 'time (ms)', - category => 'execution', + category => 'munin', title => 'Service Check Execution Times', info => 'Service Check Execution Times', }, @@ -212,7 +212,7 @@ $graphs{hosts} = { config => { args => '--lower-limit 0', vlabel => 'Host Problems', - category => 'nagios', + category => 'munin', title => 'Host Problems', info => 'Current Host Problems by Alert Status', }, @@ -228,7 +228,7 @@ $graphs{hostchkdetail} = { config => { args => '--lower-limit 0', vlabel => 'Total Number of Host Checks', - category => 'details', + category => 'munin', title => 'Detailed Host Info', info => 'Detailed Host Check Information', }, @@ -247,7 +247,7 @@ $graphs{hostchksc} = { config => { args => '--lower-limit 0 --upper-limit 100', vlabel => '%', - category => 'statechange', + category => 'munin', title => 'Host State Change', info => 'Total Percent of State Change between checks', }, @@ -263,7 +263,7 @@ $graphs{hostchklat} = { config => { args => '--lower-limit 0', vlabel => 'time (ms)', - category => 'latency', + category => 'munin', title => 'Host Check Latency Times', info => 'Host Check Latency Times', }, @@ -279,7 +279,7 @@ $graphs{hostchkext} = { config => { args => '--lower-limit 0', vlabel => 'time (ms)', - category => 'execution', + category => 'munin', title => 'Host Check Execution Times', info => 'Host Check Execution Times', }, @@ -295,7 +295,7 @@ $graphs{checks} = { config => { args => '--lower-limit 0', vlabel => 'Total Number of Checks', - category => 'nagios', + category => 'munin', title => 'Totals', info => 'Total Number of Service and Host Checks', }, @@ -310,7 +310,7 @@ $graphs{hostchkactcount} = { config => { args => '--lower-limit 0', vlabel => 'Number Host Checks', - category => 'active', + category => 'munin', title => 'Host Checks', info => 'Total Number of Active Host Checks', order => 'NUMHSTACTCHK60M NUMHSTACTCHK15M NUMHSTACTCHK5M NUMHSTACTCHK1M', @@ -328,7 +328,7 @@ $graphs{hostchkpsvcount} = { config => { args => '--lower-limit 0', vlabel => 'Number Host Checks', - category => 'passive', + category => 'munin', title => 'Host Checks', info => 'Total Number of Passive Host Checks', order => 'NUMHSTPSVCHK60M NUMHSTPSVCHK15M NUMHSTPSVCHK5M NUMHSTPSVCHK1M', @@ -346,7 +346,7 @@ $graphs{svcchkactcount} = { config => { args => '--lower-limit 0', vlabel => 'Number of Service Checks', - category => 'active', + category => 'munin', title => 'Service Checks', info => 'Total Number of Active Service Checks', order => 'NUMSVCACTCHK60M NUMSVCACTCHK15M NUMSVCACTCHK5M NUMSVCACTCHK1M', @@ -364,7 +364,7 @@ $graphs{svcchkpsvcount} = { config => { args => '--lower-limit 0', vlabel => 'Number of Service Checks', - category => 'passive', + category => 'munin', title => 'Service Checks', info => 'Total Number of Passive Service Checks', order => 'NUMSVCPSVCHK60M NUMSVCPSVCHK15M NUMSVCPSVCHK5M NUMSVCPSVCHK1M', @@ -382,7 +382,7 @@ $graphs{extcmdcount} = { config => { args => '--lower-limit 0', vlabel => 'Number of Ext Command Slots', - category => 'externalcmds', + category => 'munin', title => 'External Commands', info => 'External Command Buffer Slot Information', }, diff --git a/plugins/nagios/nagiosstatus b/plugins/nagios/nagiosstatus index 55eced93..57200814 100755 --- a/plugins/nagios/nagiosstatus +++ b/plugins/nagios/nagiosstatus @@ -54,7 +54,7 @@ if ($ARGV[0]) { } elsif ($ARGV[0] eq "config") { print "graph_args --base 1000 -l 0 --vertical-label Checks\n"; print "graph_title Nagios status\n"; - print "graph_category Nagios\n"; + print "graph_category munin\n"; print "graph_info This graph shows how many Nagios checks are in warning or critical state.\n"; print "graph_order hdown hunknown swarning scritical sunknown\n"; print "hdown.label Hosts down\n"; diff --git a/plugins/snmp/snmp__netapp_cifs b/plugins/netapp/snmp__netapp_cifs similarity index 98% rename from plugins/snmp/snmp__netapp_cifs rename to plugins/netapp/snmp__netapp_cifs index f84ac53b..75e7365a 100755 --- a/plugins/snmp/snmp__netapp_cifs +++ b/plugins/netapp/snmp__netapp_cifs @@ -33,7 +33,7 @@ if len(sys.argv) == 2 and sys.argv[1] == "config": print 'graph_title CIFS usage on '+servername print 'graph_args --base 1000 -l 0' print 'graph_vlabel number' - print 'graph_category netapp' + print 'graph_category fs' print 'graph_info This graph shows CIFS usage on '+servername print 'cifsConnectedUsers.label ConnectedUsers' diff --git a/plugins/snmp/snmp__netapp_cifs2 b/plugins/netapp/snmp__netapp_cifs2 similarity index 98% rename from plugins/snmp/snmp__netapp_cifs2 rename to plugins/netapp/snmp__netapp_cifs2 index 31686e53..40814761 100755 --- a/plugins/snmp/snmp__netapp_cifs2 +++ b/plugins/netapp/snmp__netapp_cifs2 @@ -84,7 +84,7 @@ if (defined $ARGV[0] and $ARGV[0] eq "config") print "graph_title $host CIFS sessions\n"; print "graph_args --base 1000 --lower-limit 0 --rigid\n"; print "graph_vlabel CIFS\n"; - print "graph_category netapp\n"; + print "graph_category fs\n"; print "graph_info This graph shows CIFS sessions status for the $host.\n"; print "graph_order "; foreach (sort keys %oids) diff --git a/plugins/snmp/snmp__netapp_cifscalls b/plugins/netapp/snmp__netapp_cifscalls similarity index 98% rename from plugins/snmp/snmp__netapp_cifscalls rename to plugins/netapp/snmp__netapp_cifscalls index 4ad9e97a..5ff673d3 100755 --- a/plugins/snmp/snmp__netapp_cifscalls +++ b/plugins/netapp/snmp__netapp_cifscalls @@ -91,7 +91,7 @@ if (defined $ARGV[0] and $ARGV[0] eq "config") print "graph_title $host CIFS calls\n"; print "graph_args --base 1000 --lower-limit 0 --rigid\n"; print "graph_vlabel calls / \${graph_period}\n"; - print "graph_category netapp\n"; + print "graph_category fs\n"; print "graph_info This graph shows cifs calls for the $host NetApp equipment.\n"; print "graph_order "; foreach (sort keys %oids) diff --git a/plugins/snmp/snmp__netapp_cpu b/plugins/netapp/snmp__netapp_cpu similarity index 97% rename from plugins/snmp/snmp__netapp_cpu rename to plugins/netapp/snmp__netapp_cpu index 11c7a438..ea56b09b 100755 --- a/plugins/snmp/snmp__netapp_cpu +++ b/plugins/netapp/snmp__netapp_cpu @@ -34,7 +34,7 @@ if len(sys.argv) == 2 and sys.argv[1] == "config": print 'graph_scale no' print 'graph_info This graph shows how CPU time is spent.' print 'graph_period second' - print 'graph_category netapp' + print 'graph_category cpu' print 'usage.label cpu_usage ' print 'usage.draw STACK' diff --git a/plugins/snmp/snmp__netapp_cpu2 b/plugins/netapp/snmp__netapp_cpu2 similarity index 99% rename from plugins/snmp/snmp__netapp_cpu2 rename to plugins/netapp/snmp__netapp_cpu2 index f7ab8c1d..651a2077 100755 --- a/plugins/snmp/snmp__netapp_cpu2 +++ b/plugins/netapp/snmp__netapp_cpu2 @@ -93,7 +93,7 @@ if (defined $ARGV[0] and $ARGV[0] eq "config") print "graph_title $host CPU \n"; print "graph_args --base 1000 -r --lower-limit 0 --upper-limit 100\n"; print "graph_vlabel CPU \n"; - print "graph_category netapp\n"; + print "graph_category cpu\n"; print "graph_info This graph shows cpu busy value for the $host in percent.\n"; print "graph_order "; foreach (sort keys %oids) diff --git a/plugins/snmp/snmp__netapp_diskbusy b/plugins/netapp/snmp__netapp_diskbusy similarity index 97% rename from plugins/snmp/snmp__netapp_diskbusy rename to plugins/netapp/snmp__netapp_diskbusy index 9ecab19c..35a4b262 100755 --- a/plugins/snmp/snmp__netapp_diskbusy +++ b/plugins/netapp/snmp__netapp_diskbusy @@ -63,7 +63,7 @@ sub do_collect sub do_config_root { - # graph_category netapp # To show plugin in Gallery also in this category + # graph_category san # To show plugin in Gallery also in this category my ($host) = @_; diff --git a/plugins/snmp/snmp__netapp_diskusage2_ b/plugins/netapp/snmp__netapp_diskusage2_ similarity index 98% rename from plugins/snmp/snmp__netapp_diskusage2_ rename to plugins/netapp/snmp__netapp_diskusage2_ index 4f23daa0..4f76d021 100755 --- a/plugins/snmp/snmp__netapp_diskusage2_ +++ b/plugins/netapp/snmp__netapp_diskusage2_ @@ -135,7 +135,7 @@ sub do_config_vol } print "graph_args --base 1024 --lower-limit 0\n"; print "graph_vlabel bytes\n"; - # graph_category netapp # To show plugin in Gallery also in this category + # graph_category san # To show plugin in Gallery also in this category print "graph_category disk\n"; print "graph_order df64UsedKBytes df64SnapShotUsedKBytes df64SisSavedKBytes df64TotalKBytes df64SnapShotTotalKBytes df64TotalAndSnapTotalKBytes \n"; diff --git a/plugins/snmp/snmp__netapp_diskutil b/plugins/netapp/snmp__netapp_diskutil similarity index 97% rename from plugins/snmp/snmp__netapp_diskutil rename to plugins/netapp/snmp__netapp_diskutil index b05db9ef..f6a19c57 100755 --- a/plugins/snmp/snmp__netapp_diskutil +++ b/plugins/netapp/snmp__netapp_diskutil @@ -82,7 +82,7 @@ if (defined $ARGV[0] and $ARGV[0] eq "config") print "graph_title $host Disk throughput\n"; print "graph_args --base 1024\n"; print "graph_vlabel Bytes/\${graph_period} write (-) / read (+)\n"; - # graph_category netapp # To show plugin in Gallery also in this category + # graph_category san # To show plugin in Gallery also in this category print "graph_category disk\n"; print "graph_info This graph shows DiskUtil calls for the $host NetApp equipment.\n"; print "graph_order misc64DiskWriteBytes misc64DiskReadBytes\n"; diff --git a/plugins/snmp/snmp__netapp_ndmp b/plugins/netapp/snmp__netapp_ndmp similarity index 98% rename from plugins/snmp/snmp__netapp_ndmp rename to plugins/netapp/snmp__netapp_ndmp index 2219b9e9..e37b28b4 100755 --- a/plugins/snmp/snmp__netapp_ndmp +++ b/plugins/netapp/snmp__netapp_ndmp @@ -84,7 +84,7 @@ if (defined $ARGV[0] and $ARGV[0] eq "config") print "graph_title $host NDMP \n"; print "graph_args --base 1000 --lower-limit 0 --rigid\n"; print "graph_vlabel NDMP status\n"; - print "graph_category netapp\n"; + print "graph_category backup\n"; print "graph_info This graph shows NDMP status for the $host NetApp equipment.\n"; print "graph_order "; foreach (sort keys %oids) diff --git a/plugins/snmp/snmp__netapp_net b/plugins/netapp/snmp__netapp_net similarity index 97% rename from plugins/snmp/snmp__netapp_net rename to plugins/netapp/snmp__netapp_net index f380031c..b2ab602b 100755 --- a/plugins/snmp/snmp__netapp_net +++ b/plugins/netapp/snmp__netapp_net @@ -82,7 +82,7 @@ if (defined $ARGV[0] and $ARGV[0] eq "config") print "graph_title $host Network interface traffic\n"; print "graph_args --base 1000\n"; print "graph_vlabel Bytes in (-) / out (+) per \${graph_period}\n"; - # graph_category netapp # To show plugin in Gallery also in this category + # graph_category san # To show plugin in Gallery also in this category print "graph_category network\n"; print "graph_info This graph shows net stats for the $host NetApp equipment.\n"; print "graph_order misc64NetRcvdBytes misc64NetSentBytes\n"; diff --git a/plugins/snmp/snmp__netapp_nfs3calls b/plugins/netapp/snmp__netapp_nfs3calls similarity index 97% rename from plugins/snmp/snmp__netapp_nfs3calls rename to plugins/netapp/snmp__netapp_nfs3calls index cf7eb142..44acdb21 100755 --- a/plugins/snmp/snmp__netapp_nfs3calls +++ b/plugins/netapp/snmp__netapp_nfs3calls @@ -120,8 +120,8 @@ if (defined $ARGV[0] and $ARGV[0] eq "config") { print "graph_title $host NFSv3 calls\n"; print "graph_args --base 1000\n"; print "graph_vlabel calls / \${graph_period}\n"; - # graph_category netapp # To show plugin in Gallery also in this category - print "graph_category nfs\n"; + # graph_category san # To show plugin in Gallery also in this category + print "graph_category fs\n"; print "graph_info This graph shows NFSv3 calls for the $host NetApp equipment.\n"; print "graph_order "; diff --git a/plugins/snmp/snmp__netapp_ops b/plugins/netapp/snmp__netapp_ops similarity index 98% rename from plugins/snmp/snmp__netapp_ops rename to plugins/netapp/snmp__netapp_ops index b95bcee9..a443749d 100755 --- a/plugins/snmp/snmp__netapp_ops +++ b/plugins/netapp/snmp__netapp_ops @@ -85,7 +85,7 @@ if (defined $ARGV[0] and $ARGV[0] eq "config") print "graph_title $host OPS \n"; print "graph_args --base 1000 --lower-limit 0 --rigid\n"; print "graph_vlabel OPS \n"; - print "graph_category netapp\n"; + print "graph_category fs\n"; print "graph_info This graph shows OPS for the $host NetApp equipment.\n"; print "graph_order misc64CifsOps misc64NfsOps iscsi64Ops fcp64Ops misc64HttpOps\n"; foreach my $k (sort keys %config) diff --git a/plugins/snmp/snmp__netapp_reallocate b/plugins/netapp/snmp__netapp_reallocate similarity index 99% rename from plugins/snmp/snmp__netapp_reallocate rename to plugins/netapp/snmp__netapp_reallocate index e912f648..b68abc6d 100755 --- a/plugins/snmp/snmp__netapp_reallocate +++ b/plugins/netapp/snmp__netapp_reallocate @@ -112,7 +112,7 @@ sub do_config_vol } print "graph_args --base 1000 --lower-limit 0 --rigid\n"; print "graph_vlabel reallocate_status status\n"; - print "graph_category netapp\n"; + print "graph_category fs\n"; foreach my $state ("reallocating", "redirecting", "quiesce", "debug") { print "$state.label $state\n"; diff --git a/plugins/snmp/snmp__netapp_sis b/plugins/netapp/snmp__netapp_sis similarity index 99% rename from plugins/snmp/snmp__netapp_sis rename to plugins/netapp/snmp__netapp_sis index 2c81f43d..640c2f39 100755 --- a/plugins/snmp/snmp__netapp_sis +++ b/plugins/netapp/snmp__netapp_sis @@ -110,7 +110,7 @@ sub do_config_vol } print "graph_args --base 1000 --lower-limit 0 --rigid\n"; print "graph_vlabel sis status\n"; - print "graph_category netapp\n"; + print "graph_category fs\n"; print "graph_order sisInitialising sisRunning sisPending sisDebug\n"; foreach my $state ("debug", "initialising", "running", "pending") { diff --git a/plugins/snmp/snmp__netscaler_connections b/plugins/netscaler/snmp__netscaler_connections similarity index 98% rename from plugins/snmp/snmp__netscaler_connections rename to plugins/netscaler/snmp__netscaler_connections index 717145dc..9cc822f1 100755 --- a/plugins/snmp/snmp__netscaler_connections +++ b/plugins/netscaler/snmp__netscaler_connections @@ -130,7 +130,7 @@ if ($ARGV[0] and $ARGV[0] eq "config") { print "graph_args --base 1024 -l 0\n"; print "graph_vlabel Connections\n"; print "graph_title Netscaler Connections for $o_host\n"; - print "graph_category netscaler\n"; + print "graph_category loadbalancer\n"; print "graph_period second\n"; print "graph_info This graph shows the netscaler TCP connections.\n"; print "graph_order ", @@ -148,7 +148,7 @@ if ($ARGV[0] and $ARGV[0] eq "config") { print "ssl.draw LINE2\n"; print "ssl.info Currently active SSL sessions.\n"; - for my $field qw(client server ssl) { + for my $field (qw(client server ssl)) { print_thresholds($field); } exit 0; diff --git a/plugins/snmp/snmp__netscaler_cpu b/plugins/netscaler/snmp__netscaler_cpu similarity index 95% rename from plugins/snmp/snmp__netscaler_cpu rename to plugins/netscaler/snmp__netscaler_cpu index ac81ff11..e0ad7bb5 100755 --- a/plugins/snmp/snmp__netscaler_cpu +++ b/plugins/netscaler/snmp__netscaler_cpu @@ -147,23 +147,23 @@ if ($ARGV[0] and $ARGV[0] eq "config") { print "graph_vlabel %\n"; print "graph_scale no\n"; print "graph_title CPU usage for $o_host\n"; - print "graph_category netscaler\n"; + print "graph_category loadbalancer\n"; print "graph_period second\n"; print "graph_info This graph shows how CPU time is spent.\n"; print "avg.label avg\n"; print "avg.draw AREA\n"; print "avg.info Average load.\n"; - foreach my $v (@cpu_name){ - print $v.".label ".$v."\n"; - print $v.".draw LINE2\n"; - print $v.".info CPU usage of ".$v."\n"; - print_thresholds($v); + foreach my $v (@cpu_name){ + print $v.".label ".$v."\n"; + print $v.".draw LINE2\n"; + print $v.".info CPU usage of ".$v."\n"; + print_thresholds($v); } - for my $field qw(avg) { + for my $field (qw(avg)) { print_thresholds($field); } - &close_session($session); + &close_session($session); exit 0; } diff --git a/plugins/network/bandwidth-OVH-Network b/plugins/network/bandwidth-OVH-Network index e3bed44b..880c91e6 100755 --- a/plugins/network/bandwidth-OVH-Network +++ b/plugins/network/bandwidth-OVH-Network @@ -12,6 +12,7 @@ if [ "$1" = "config" ]; then echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel MB/s' echo 'graph_scale no' + echo 'graph_category network' echo "download.label download" exit 0 fi diff --git a/plugins/network/bandwidth_ b/plugins/network/bandwidth_ index a66d61f8..09b3410c 100755 --- a/plugins/network/bandwidth_ +++ b/plugins/network/bandwidth_ @@ -88,7 +88,7 @@ init(); sub autoconf { $0 =~ /bandwidth_(.+)*$/; $interface = $1; - $history = "/var/lib/munin/plugin-state/bandwidth_$interface.state"; + $history = "$ENV{MUNIN_PLUGSTATE}/bandwidth_$interface.state"; } sub bit32or64 { diff --git a/plugins/network/dansguardian b/plugins/network/dansguardian index 536d5473..e926a8e6 100755 --- a/plugins/network/dansguardian +++ b/plugins/network/dansguardian @@ -17,7 +17,7 @@ use strict; my $QUERYLOG = $ENV{logfile} || '/var/log/dansguardian/access.log'; my $STATEFILE = $ENV{statefile} - || '/var/lib/munin/plugin-state/dansguardian.state'; + || "$ENV{MUNIN_PLUGSTATE}/dansguardian.state"; my %IN; sub get_state { diff --git a/plugins/network/ddclient b/plugins/network/ddclient old mode 100644 new mode 100755 diff --git a/plugins/network/dns/dnsresponse_ b/plugins/network/dns/dnsresponse_ index 0d3f756c..684d92bd 100755 --- a/plugins/network/dns/dnsresponse_ +++ b/plugins/network/dns/dnsresponse_ @@ -66,7 +66,7 @@ if ( defined $ARGV[0] and $ARGV[0] eq "config" ) { print "graph_title $dnsip DNS response time\n"; print "graph_vlabel milliseconds\n"; print "graph_scale no\n"; - print "graph_category Other\n"; + print "graph_category dns\n"; print "graph_info Time taken by $dnsip to resolve $site $times times.\n"; #my @val = ("min", "avg", "median", "max"); my @val = ("avg", "median", "stddev"); diff --git a/plugins/network/dns/pdns_rel b/plugins/network/dns/pdns_rel deleted file mode 100755 index 9ab79c1a..00000000 --- a/plugins/network/dns/pdns_rel +++ /dev/null @@ -1,48 +0,0 @@ -#!/bin/bash -# -# Script to monitor PowerDNS performance -# -# Parameters understood: -# -# config (required) -# autoconf (optional - used by munin-config) -#%# family=auto -#%# capabilities=autoconf - -command="/etc/init.d/pdns show" -state_file=/var/lib/munin/plugin-state/pdns_rel.state - -if [ "$1" = "autoconf" ]; then - echo yes - exit 0 -fi - -if [ "$1" = "config" ]; then - echo 'graph_title Power DNS Packet Cache Performance' - echo 'graph_args -l 0 --upper-limit 100 --base 1000' - echo 'graph_vlabel %' - echo 'graph_category Power DNS' - echo 'graph_info This graph shows the Power DNS packet cache performance on the machine.' - echo 'packetcache_hitrate.label packet cache hitrate' - echo 'packetcache_hitrate.type GAUGE' - echo 'packetcache_hitrate.min 0' - echo 'packetcache_hitrate.max 100' - echo 'packetcache_hitrate.info Hits on the packets cache' - exit 0 -fi - -hits=$($command packetcache-hit | awk -F= '{print $2}') -queries=$($command udp-queries | awk -F= '{print $2}') -old_hits=$(cat $state_file | head -n1) -old_queries=$(cat $state_file | tail -n1) - -if [ -f $state_file ] && [ $(ls -l --time-style=+%s $state_file | awk '{print $6}') -gt $(date --date="7 minutes ago" +%s) ] ; then - d_hits=$(($hits - $old_hits)) - d_queries=$(($queries - $old_queries)) - if [ $d_queries -gt 0 ] ; then - echo packetcache_hitrate.value $(( $d_hits * 100 / $d_queries )) - fi -fi - -echo $hits > $state_file -echo $queries >> $state_file diff --git a/plugins/network/netstat_s_/netstat_s_tcp_connections_connections-day-linux.png b/plugins/network/example-graphs/netstat_s_-day.png similarity index 100% rename from plugins/network/netstat_s_/netstat_s_tcp_connections_connections-day-linux.png rename to plugins/network/example-graphs/netstat_s_-day.png diff --git a/plugins/network/netstat_s_/netstat_s_tcp_connections_connections-month-freebsd.png b/plugins/network/example-graphs/netstat_s_-month.png similarity index 100% rename from plugins/network/netstat_s_/netstat_s_tcp_connections_connections-month-freebsd.png rename to plugins/network/example-graphs/netstat_s_-month.png diff --git a/plugins/network/example-graphs/olsr-link_quality_day.png b/plugins/network/example-graphs/olsr-link_quality_day.png new file mode 100644 index 00000000..512c1cf6 Binary files /dev/null and b/plugins/network/example-graphs/olsr-link_quality_day.png differ diff --git a/plugins/network/example-graphs/olsr-neighbour_link_count_day.png b/plugins/network/example-graphs/olsr-neighbour_link_count_day.png new file mode 100644 index 00000000..223a784a Binary files /dev/null and b/plugins/network/example-graphs/olsr-neighbour_link_count_day.png differ diff --git a/plugins/network/example-graphs/tc_-day.png b/plugins/network/example-graphs/tc_-day.png new file mode 100644 index 00000000..6607b71d Binary files /dev/null and b/plugins/network/example-graphs/tc_-day.png differ diff --git a/plugins/network/example-graphs/tc_-week.png b/plugins/network/example-graphs/tc_-week.png new file mode 100644 index 00000000..50557672 Binary files /dev/null and b/plugins/network/example-graphs/tc_-week.png differ diff --git a/plugins/network/fwbuilder_ b/plugins/network/fwbuilder_ index d955eea6..23f8ba3c 100755 --- a/plugins/network/fwbuilder_ +++ b/plugins/network/fwbuilder_ @@ -7,7 +7,7 @@ # # ...will monitor the IP 192.168.0.1. # -# Aditionally, you need Accountingrules in fwbuilder +# Additionally, you need Accountingrules in fwbuilder # fwbuilder creates Chains in INPUT-, OUTPUT- and FORWARD-Chain # with Rules that "RETURN" # You will have to specify rule options with name "ACCOUNTING" for the @@ -61,7 +61,7 @@ IP=${IP/-/\/} if [ "$1" = "autoconf" ]; then if [ -r /proc/net/dev ]; then - iptables -L INPUT -v -n -x >/dev/null 2>/dev/null + iptables -L INPUT -v -n -x -w >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "no (could not run iptables as user `whoami`)" exit 1 @@ -77,7 +77,7 @@ fi if [ "$1" = "suggest" ]; then # find Chains for Accounting - iptables -L -n |grep ^ACCOUNTING |awk '{printf "%s\n%s\n",$4,$5}'| sort -u |sed 's#\/#-#' + iptables -L -n -w | grep ^ACCOUNTING |awk '{printf "%s\n%s\n",$4,$5}'| sort -u |sed 's#\/#-#' exit 0 fi @@ -87,7 +87,7 @@ if [ "$1" = "config" ]; then echo "graph_title $IP traffic" echo 'graph_args --base 1024' echo 'graph_vlabel bytes per ${graph_period}' - echo 'graph_category network' + echo 'graph_category fw' echo 'out.label sent' echo 'out.type DERIVE' echo 'out.min 0' @@ -99,6 +99,6 @@ if [ "$1" = "config" ]; then exit 0 fi; -echo "in.value $(( $(iptables -L -n -v -x |grep "ACCOUNTING" |awk '{printf "%s %s\n",$2,$9}' |grep $IP |awk '{printf "%s + ",$1}') 0 ))" -echo "out.value $(( $(iptables -L -n -v -x |grep "ACCOUNTING" |awk '{printf "%s %s\n",$2,$8}' |grep $IP |awk '{printf "%s + ",$1}') 0 ))" +echo "in.value $(( $(iptables -L -n -v -x -w | grep "ACCOUNTING" | awk '{printf "%s %s\n",$2,$9}' | grep $IP | awk '{printf "%s + ",$1}') 0 ))" +echo "out.value $(( $(iptables -L -n -v -x -w |grep "ACCOUNTING" | awk '{printf "%s %s\n",$2,$8}' | grep $IP | awk '{printf "%s + ",$1}') 0 ))" diff --git a/plugins/network/if b/plugins/network/if index 06af4154..d5086c85 100755 --- a/plugins/network/if +++ b/plugins/network/if @@ -120,7 +120,7 @@ my $graphs = { 'munin' => { - 'category' => 'wifi', + 'category' => 'wireless', 'args' => '--base 1000 -u 0', 'title' => ':if: signal and noise levels', 'vlabel' => 'dB', @@ -134,7 +134,7 @@ my $graphs = { 'munin' => { - 'category' => 'wifi', + 'category' => 'wireless', 'args' => '--base 1000', 'title' => ':if: link quality', 'vlabel' => '%', @@ -148,7 +148,7 @@ my $graphs = { 'munin' => { - 'category' => 'wifi', + 'category' => 'wireless', 'args' => '--base 1000', 'title' => ':if: errors', 'vlabel' => 'Errors RX (-) / TX (+)', diff --git a/plugins/system/1sec/if1sec-c.c b/plugins/network/if1sec-c.c similarity index 94% rename from plugins/system/1sec/if1sec-c.c rename to plugins/network/if1sec-c.c index 6793be4c..4786d956 100644 --- a/plugins/system/1sec/if1sec-c.c +++ b/plugins/network/if1sec-c.c @@ -38,8 +38,8 @@ char* get_ifname_from_procstatline(char* line) { int config() { /* Get the number of if */ - int f; - if ( !(f=open(PROC_STAT, O_RDONLY)) ) { + int f = open(PROC_STAT, O_RDONLY); + if ( f == -1 ) { return fail("cannot open " PROC_STAT); } @@ -66,7 +66,7 @@ int config() { "multigraph if_%s_1sec" "\n" "graph_order down up" "\n" "graph_title %s traffic" "\n" - "graph_category system::1sec" "\n" + "graph_category 1sec" "\n" "graph_vlabel bits in (-) / out (+) per ${graph_period}" "\n" "graph_data_size custom 1d, 10s for 1w, 1m for 1t, 5m for 1y" "\n" , if_name, if_name @@ -120,7 +120,12 @@ int acquire() { /* fork ourselves if not asked otherwise */ char* no_fork = getenv("no_fork"); if (! no_fork || strcmp("1", no_fork)) { - if (fork()) return; + pid_t child_pid = fork(); + if (child_pid) { + printf("# acquire() launched as PID %d\n", child_pid); + return 0; + } + // we are the child, complete the daemonization /* Close standard IO */ @@ -139,9 +144,15 @@ int acquire() { /* Reading /proc/stat */ int f = open(PROC_STAT, O_RDONLY); + if ( f == -1 ) { + return fail("cannot open " PROC_STAT); + } /* open the spoolfile */ int cache_file = open(cache_filename, O_CREAT | O_APPEND | O_WRONLY, S_IRUSR | S_IWUSR); + if ( cache_file == -1 ) { + return fail("# cannot open cache_file"); + } /* loop each second */ while (1) { @@ -213,6 +224,9 @@ int acquire() { int fetch() { FILE* cache_file = fopen(cache_filename, "r+"); + if ( !cache_file ) { + return acquire(); + } /* lock */ flock(fileno(cache_file), LOCK_EX); diff --git a/plugins/network/interfaces_linux_multi b/plugins/network/interfaces_linux_multi index ad158600..2b75c82c 100755 --- a/plugins/network/interfaces_linux_multi +++ b/plugins/network/interfaces_linux_multi @@ -102,7 +102,6 @@ if (!defined $ENV{MUNIN_PLUGSTATE}) { sub pidfile() { "$ENV{MUNIN_PLUGSTATE}/munin.$plugin.pid" } sub cachefile() { "$ENV{MUNIN_PLUGSTATE}/munin.$plugin.cache" } -sub graph_section() { "system:network" } sub graph_name() { "interfaces" } #sub graph_title() { "interfaces" } #sub graph_title_all() { "Overall CPU usage" } @@ -327,7 +326,7 @@ sub show_config() } print </dev/null 2>/dev/null + iptables -L INPUT -v -n -x -w >/dev/null 2>/dev/null if [ $? -gt 0 ]; then echo "no (could not run iptables as user `whoami`)" exit 1 @@ -57,7 +57,7 @@ if [ "$1" = "autoconf" ]; then fi if [ "$1" = "suggest" ]; then - iptables -L INPUT -v -x -n 2>/dev/null | sed -n 's/^.*\/\* ACC\-\([a-zA-Z]*\) \*\/.*$/\1/p' + iptables -L INPUT -v -x -n -w 2>/dev/null | sed -n 's/^.*\/\* ACC\-\([a-zA-Z]*\) \*\/.*$/\1/p' exit 0 fi @@ -79,5 +79,5 @@ if [ "$1" = "config" ]; then exit 0 fi; -iptables -L INPUT -v -n -x | grep -m1 "\/\* ACC\-"$ACC" \*\/" | awk "{ print \"in.value \" \$2 }" -iptables -L OUTPUT -v -n -x | grep -m1 "\/\* ACC\-"$ACC" \*\/" | awk "{ print \"out.value \" \$2 }" +iptables -L INPUT -v -n -x -w | grep -m1 "\/\* ACC\-"$ACC" \*\/" | awk "{ print \"in.value \" \$2 }" +iptables -L OUTPUT -v -n -x -w | grep -m1 "\/\* ACC\-"$ACC" \*\/" | awk "{ print \"out.value \" \$2 }" diff --git a/plugins/network/ipt_basic_ b/plugins/network/ipt_basic_ index 6f05638c..ec17102f 100755 --- a/plugins/network/ipt_basic_ +++ b/plugins/network/ipt_basic_ @@ -45,7 +45,7 @@ iptables='/sbin/iptables' if [ "$1" = "autoconf" ]; then if [ -r /proc/net/dev ]; then - RES=`$iptables -L $TNAME -nvx 2>&1 >/dev/null` + RES=`$iptables -L $TNAME -nvx -w 2>&1 >/dev/null` if [ $? -gt 0 ]; then echo "no (could not run iptables as user `whoami`; $RES)" exit 1 @@ -79,7 +79,7 @@ if [ "$1" = "initialise" ]; then exit 1 fi -IFACES=`$iptables -L munin_node -nvx | awk '$6 ~ /(eth|ppp)[0-9]/ { if (done[$6]!=1) {print $6; done[$6]=1;}}'` +IFACES=`$iptables -L munin_node -nvx -w | awk '$6 ~ /(eth|ppp)[0-9]/ { if (done[$6]!=1) {print $6; done[$6]=1;}}'` if [ "$1" = "config" ]; then @@ -108,7 +108,7 @@ if [ "$1" = "config" ]; then fi; if [ "$TYPE" = "pkts" ]; then - $iptables -L munin_node -nvx | egrep "eth|ppp" | awk "{ print \$6 \".value \" \$1 }" + $iptables -L munin_node -nvx -w | egrep "eth|ppp" | awk "{ print \$6 \".value \" \$1 }" else - $iptables -L munin_node -nvx | egrep "eth|ppp" | awk "{ print \$6 \".value \" \$2 }" + $iptables -L munin_node -nvx -w | egrep "eth|ppp" | awk "{ print \$6 \".value \" \$2 }" fi diff --git a/plugins/network/linux_if/linux_if_bonding.png b/plugins/network/linux_if/example-graphs/linux_if-1.png similarity index 100% rename from plugins/network/linux_if/linux_if_bonding.png rename to plugins/network/linux_if/example-graphs/linux_if-1.png diff --git a/plugins/network/linux_if/linux_if_vlans.png b/plugins/network/linux_if/example-graphs/linux_if-2.png similarity index 100% rename from plugins/network/linux_if/linux_if_vlans.png rename to plugins/network/linux_if/example-graphs/linux_if-2.png diff --git a/plugins/network/linux_if/linux_if b/plugins/network/linux_if/linux_if index b418f675..e9d6b89e 100755 --- a/plugins/network/linux_if/linux_if +++ b/plugins/network/linux_if/linux_if @@ -27,7 +27,7 @@ plugin configuration: # example: env.include = br_* - # should staticly configured interfaces be included (they have ifcfg-* file) + # should statically configured interfaces be included (they have ifcfg-* file) # default: true env.include_configured_if = true @@ -41,7 +41,7 @@ Include/exclude logic in detail. Interface name is matched.. Tested on: RHEL 6.x and clones (with Python 2.6) TODO: -* implment 'data loaning' between graphs, removes duplicit measures +* implement 'data loaning' between graphs, removes duplicit measures * add support for bridging * configurable graph max based on intreface speed diff --git a/plugins/network/mtr100_ b/plugins/network/mtr100_ index 2e439519..44ed34f3 100755 --- a/plugins/network/mtr100_ +++ b/plugins/network/mtr100_ @@ -5,7 +5,7 @@ # # Version: 1.0 # Author: tobias.geiger@vido.info -# Please email me bugs/suggestions +# Please email me bugs/suggestions # # Version: 1.1 # Author: charlie@evilforbeginners.com @@ -21,8 +21,8 @@ # # Parameters: # -# config (required) -# autoconf (optional - only used by munin-config) +# config (required) +# autoconf (optional - only used by munin-config) # # Magic markers (optional - used by munin-config and some installation # scripts): @@ -32,25 +32,25 @@ totrace=`basename $0 | sed 's/^mtr100_//g'` if [ "$1" = "autoconf" ]; then - if ( mtr -nrc 1 localhost 2>/dev/null >/dev/null ); then - echo yes - exit 0 - else - if [ $? -eq 127 ] - then - echo "no (mtr program not found - install the mtr(-tiny) package)" - exit 1 - else - echo no - exit 1 - fi - fi + if ( mtr -nrc 1 localhost 2>/dev/null >/dev/null ); then + echo yes + exit 0 + else + if [ $? -eq 127 ] + then + echo "no (mtr program not found - install the mtr(-tiny) package)" + exit 1 + else + echo no + exit 1 + fi + fi exit 0 fi dotrace() { -LC_ALL=C mtr -nrs 1024 -c 5 $totrace | grep -vi -E "^HOST:|^Start:" | LC_ALL=C awk -v C=$1 ' { +LC_ALL=C mtr -nrs 1024 -c 5 $totrace | grep -vi -E "^HOST:|^Start:" | LC_ALL=C awk -v C=$1 -v SRC_HOSTNAME=$(hostname) ' { label=$2 x=gsub("\\.","_",label) @@ -63,27 +63,29 @@ total+=$6 } END { - for (x=1; x<=count; x++) { - value=(val[x]/total)*100 - if ( C != "config" ) { printf "%s.value %2.2f\n","hop_" lab[x],value } - if ( C == "config" ) { print "hop_" lab[x] ".label " name[x] } - if ( C == "config" ) { if ( x == 1 ) { print "hop_" lab[x]".draw AREA" } else { print "hop_" lab[x]".draw STACK" } } - } + # Hard code the first hop (hop_0) as the local hostname + if ( C != "config" ) { print "hop_0.value U" } + if ( C == "config" ) { print "hop_0.label " SRC_HOSTNAME } + if ( C == "config" ) { print "hop_0.draw AREA" } + for (x=1; x<=count; x++) { + value=(val[x]/total)*100 + if ( C != "config" ) { printf "%s.value %2.2f\n","hop_" lab[x],value } + if ( C == "config" ) { print "hop_" lab[x] ".label " name[x] } + if ( C == "config" ) { print "hop_" lab[x]".draw STACK" } + } }' } if [ "$1" = "config" ]; then - - echo 'graph_title Traceroute (%) to '$totrace - echo 'graph_args --base 1000 -l 0 -u 100 -r' - echo 'graph_vlabel ms (percentage)' - echo 'graph_category network' - echo 'graph_scale no' - echo 'graph_period second' - echo 'graph_info This graph shows the Percentage needed for each hop on the way to '$totrace - dotrace config; - exit 0 + echo 'graph_title Traceroute (%) to '$totrace + echo 'graph_args --base 1000 -l 0 -u 100 -r' + echo 'graph_vlabel ms (percentage)' + echo 'graph_category network' + echo 'graph_scale no' + echo 'graph_period second' + echo 'graph_info This graph shows the Percentage needed for each hop on the way to '$totrace + dotrace config; + exit 0 else - dotrace; + dotrace; fi - diff --git a/plugins/network/multibandwidth b/plugins/network/multibandwidth new file mode 100755 index 00000000..a7edf4c8 --- /dev/null +++ b/plugins/network/multibandwidth @@ -0,0 +1,112 @@ +#!/bin/sh + +. "$MUNIN_LIBDIR/plugins/plugin.sh" + +: <<=cut + +=head1 NAME + +multibandwidth - Plugin to monitor the bandwidth between localhost and serveral hosts. + +=head1 APPLICABLE SYSTEMS + +All systems with “bash”, and “munin” + +=head1 REQUIREMENTS + +bing installed. + +You can install bing by using (Ubuntu/Debian): apt-get install bing + +=head1 CONFIGURATION + +The following is the default configuration + +[multibandwidth] +user root +env.hosts example.org example2.org example3.org +env.samples 10 +env.small_packet_size 44 +env.big_packet_size 108 + +- env.hosts explanation: hostname or IP of the hosts to calculate the bandwidth. + +- env.samples explanation: Reset stats after sending samples ECHO_REQUEST packets. + +- env.small_packet_size explanation: Specifies the number of data bytes to be sent in the small + packets. The default and minimum value is 44. + +- env.big_packet_size explanation: Specifies the number of data bytes to be sent in the big + packets. The default is 108. The size should be chosen so that big packet roundtrip times + are long enough to be accurately measured. + + +=head1 MAGIC MARKERS + +#%# capabilities=autoconf + +=head1 VERSION + +1.1.17 + +=head1 AUTHOR + +Jose Manuel Febrer Cortés +Marco Bertola’s help + +=head1 LICENSE + +GPLv2 + +=cut + + + +case $1 in + config) + echo graph_title MultiBandwidth + echo 'graph_vlabel bps' + echo 'graph_args --base 1024 -l 0' + echo 'graph_scale yes' + echo 'graph_category network' + echo 'graph_info This graph shows the bandwidth between localhost and serveral hosts' + for host in $hosts; do + fieldname="host_$(clean_fieldname "$host")" + echo "$fieldname.label $host" + echo "$fieldname.draw LINE2" + echo "$fieldname.info Bandwidth statistics for $host" + done + exit 0;; + autoconf) + if command -v bing >/dev/null 2>&1; then + echo 'yes' + exit 0; + else + echo 'no (bing not installed)' + exit 0; + fi + +esac + + +#Calculating the bandwidth +for host in $hosts; do + fieldname="host_$(clean_fieldname "$host")" + printf "$fieldname.value "; + + SPEED=$(timeout 6 bing localhost "$host" -n -c 1 -e "$samples" -s "$small_packet_size" -S "$big_packet_size" 2>/dev/null \ + |grep "estimated link" -A 2 \ + | grep bps \ + | awk '{print $2}' \ + | cut -d "b" -f1) + + if (echo "$SPEED" | grep -q "M"); then + echo "$SPEED" | awk '{a+=$1} END{print a*1000000}' + elif (echo "$SPEED" | grep -q "K"); then + echo "$SPEED" | awk '{a+=$1} END{print a*1000}' + elif (echo "$SPEED" | grep -q "G"); then + echo "$SPEED" | awk '{a+=$1} END{print a*1000000000}' + else + echo "Error: no data (timeout)" >&2 + fi +done diff --git a/plugins/other/multipng_async b/plugins/network/multiping_async similarity index 100% rename from plugins/other/multipng_async rename to plugins/network/multiping_async diff --git a/plugins/network/netatalk b/plugins/network/netatalk index 6384b1e1..9ac211eb 100755 --- a/plugins/network/netatalk +++ b/plugins/network/netatalk @@ -69,7 +69,7 @@ if [ "$1" = "config" ]; then echo 'graph_title Netatalk status' echo 'graph_args --logarithmic --lower-limit 0.1' echo 'graph_vlabel Number' - echo 'graph_category network' + echo 'graph_category fs' echo 'proc.label Running processes' echo 'proc.info Number of running afpd processes' echo 'proc.min 0' diff --git a/plugins/network/netatalk3 b/plugins/network/netatalk3 index db05183b..fc766804 100755 --- a/plugins/network/netatalk3 +++ b/plugins/network/netatalk3 @@ -47,7 +47,7 @@ function count_running_procs () { function count_connected_users () { local afpd_bin_path=$1 afpd_procs=$(ps anx --no-headers -o uid,command |grep -E "\w+\d+*\s${afpd_bin_path}" |wc -l) - # one of those processess will be always from root user, so it's not being + # one of those processes will be always from root user, so it's not being # used to externaly connect volumes, therefor being disconsider. echo $(echo "${afpd_procs} - 1" |bc) } @@ -99,7 +99,7 @@ graph_title Netatalk v3 Status graph_title Netatalk status graph_args --logarithmic --lower-limit 0.1 graph_vlabel Number -graph_category network +graph_category fs proc.label Running processes proc.info Running AFPd processes proc.min 0 @@ -119,7 +119,7 @@ EOM #### Boilerplates #################################################### # Locating AFP related files: # * Binary: Using the first result of "which" command; - # * Config: Using "afpd" with paremeters to pring configuration file + # * Config: Using "afpd" with parameters to pring configuration file # location; # diff --git a/plugins/network/netstat_s_/netstat_s_ b/plugins/network/netstat_s_ similarity index 100% rename from plugins/network/netstat_s_/netstat_s_ rename to plugins/network/netstat_s_ diff --git a/plugins/network/olsrd b/plugins/network/olsrd new file mode 100755 index 00000000..e74568d0 --- /dev/null +++ b/plugins/network/olsrd @@ -0,0 +1,402 @@ +#!/bin/sh +# weird shebang? See below: "interpreter selection" + +"""true" +: <<=cut + +=head1 NAME + +olsrd - Monitor the state of an OLSR-based routing network + + +=head1 APPLICABLE SYSTEMS + +Information is parsed from the output of "txtinfo" plugin for olsrd. + + +=head1 CONFIGURATION + +Environment variables: + + * OLSRD_HOST: name or IP of the host running the txtinfo plugin (default: localhost) + * OLSRD_TXTINFO_PORT: the port that the txtinfo plugin is listening to (default: 2006) + * OLSRD_BIN_PATH: name of the olsrd binary (only used for 'autoconf', default: /usr/sbin/olsrd) + * MICROPYTHON_HEAP: adjust this parameter for micropython if your olsr network contains + more than a few thousand nodes (default: 512k) + +=head1 USAGE + +Collect basic information about the neighbours of an OLSR node: + + * link quality + * neighbour link quality + * number of nodes reachable behind each neighbour + * ping times of direct neighbours + +This plugin works with the following python interpreters: + + * Python 2 + * Python 3 + * micropython (e.g. OpenWrt) + + +=head1 VERSION + + 0.4 + + +=head1 AUTHOR + +Lars Kruse + + +=head1 LICENSE + +GPLv3 or above + + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=cut + + +# ****************** Interpreter Selection *************** +# This unbelievable dirty hack allows to find a suitable python interpreter. +# This is specifically useful for OpenWRT where typically only micropython is available. +# +# Additionally we need to run micropython with additional startup options. +# This is necessary due to our demand for more than 128k heap (this default is sufficient for only +# 400 olsr nodes). +# +# This "execution hack" works as follows: +# * the script is executed by busybox ash or another shell +# * the above line (three quotes before and one quote after 'true') evaluates differently for +# shell and python: +# * shell: run "true" (i.e. nothing happens) +# * python: ignore everything up to the next three consecutive quotes +# Thus we may place shell code here that will take care for selecting an interpreter. + +# prefer micropython if it is available - otherwise fall back to any python (2 or 3) +MICROPYTHON_BIN=$(which micropython || true) +if [ -n "$MICROPYTHON_BIN" ]; then + "$MICROPYTHON_BIN" -X "heapsize=${MICROPYTHON_HEAP:-512k}" "$0" "$@" +else + python "$0" "$@" +fi +exit $? + +# For shell: ignore everything starting from here until the last line of this file. +# This is necessary for syntax checkers that try to complain about invalid shell syntax below. +true <= 2: + line = line.strip() + if line: + yield line + in_body_count += 1 + fconn.close() + conn.close() + + +def get_address_device_mapping(): + mapping = {} + for line in query_olsrd_txtservice("mid"): + # example line content: + # 192.168.2.171 192.168.22.171;192.168.12.171 + # since olsr v0.9.5: + # 192.168.2.171 192.168.22.171 192.168.12.171 + device_id, mids = line.split(None, 1) + for mid in mids.replace(";", " ").split(): + mapping[mid] = device_id + return mapping + + +def count_routes_by_neighbour(address_mapping, ignore_list): + node_count = {} + for line in query_olsrd_txtservice("rou"): + # example line content: + # 192.168.1.79/32 192.168.12.38 4 4.008 wlan0 + tokens = line.split() + target = tokens[0] + via = tokens[1] + # we care only about single-host routes + if target.endswith("/32"): + if target[:-3] in address_mapping: + # we ignore MIDs - we want only real nodes + continue + if target in ignore_list: + continue + # replace the neighbour's IP with its main IP (if it is an MID) + via = address_mapping.get(via, via) + # increase the counter + node_count[via] = node_count.get(via, 0) + 1 + return node_count + + +def get_olsr_links(): + mid_mapping = get_address_device_mapping() + hna_list = [line.split()[0] for line in query_olsrd_txtservice("hna")] + route_count = count_routes_by_neighbour(mid_mapping, hna_list) + result = [] + for line in query_olsrd_txtservice("lin"): + tokens = line.split() + # the "cost" may be infinite + if tokens[-1] == "INFINITE": + # "inf" is the python keyword for "maximum float number" + tokens[-1] = "inf" + link = {} + link["local"] = tokens.pop(0) + remote = tokens.pop(0) + # replace the neighbour's IP with its main IP (if it is an MID) + link["remote"] = mid_mapping.get(remote, remote) + for key in ("hysterese", "lq", "nlq", "cost"): + link[key] = float(tokens.pop(0)) + # add the route count + link["route_count"] = route_count.get(link["remote"], 0) + result.append(link) + result.sort(key=lambda link: link["remote"]) + return result + + +def _read_file(filename): + try: + return open(filename, "r").read().split(LINESEP) + except OSError: + return [] + + +def get_ping_times(hosts): + tempfile = "/tmp/munin-olsrd-{pid}.tmp".format(pid=os.getpid()) + command = ('for host in {hosts}; do echo -n "$host "; ' + 'ping -c 1 -w 1 "$host" | grep /avg/ || echo; done >{tempfile}' + .format(hosts=" ".join(hosts), tempfile=tempfile)) + # micropython supports only "os.system" (as of 2015) - thus we need to stick with it for + # OpenWrt. + returncode = os.system(command) + if returncode != 0: + return {} + lines = _read_file(tempfile) + os.unlink(tempfile) + # example output for one host: + # 192.168.2.41 round-trip min/avg/max = 4.226/4.226/4.226 ms + result = {} + for line in lines: + tokens = line.split(None) + if len(tokens) > 1: + host = tokens[0] + avg_ping = tokens[-2].split("/")[1] + result[host] = float(avg_ping) + return result + + +def do_config(): + links = list(get_olsr_links()) + + # link quality with regard to neighbours + print("multigraph olsr_link_quality") + print(LQ_GRAPH_CONFIG.format(title="OLSR Link Quality")) + for link in links: + print(LQ_VALUES_CONFIG.format( + label=link["remote"], + suffix="_{host}".format(host=get_clean_fieldname(link["remote"])), + draw_type="AREASTACK")) + for link in links: + print("multigraph olsr_link_quality.host_{remote}" + .format(remote=get_clean_fieldname(link["remote"]))) + title = "Link Quality towards {host}".format(host=link["remote"]) + print(LQ_GRAPH_CONFIG.format(title=title)) + print(LQ_VALUES_CONFIG.format(label="Link Quality", suffix="", draw_type="AREA")) + + # link count ("number of nodes behind each neighbour") + print("multigraph olsr_neighbour_link_count") + print(NEIGHBOUR_COUNT_CONFIG) + for link in links: + print(NEIGHBOUR_COUNT_VALUE + .format(host=link["remote"], host_fieldname=get_clean_fieldname(link["remote"]), + draw_type="AREASTACK")) + + # neighbour ping + print("multigraph olsr_neighbour_ping") + print(NEIGHBOUR_PING_CONFIG.format(title="Ping time of neighbours")) + for link in links: + print(NEIGHBOUR_PING_VALUE + .format(host=link["remote"], host_fieldname=get_clean_fieldname(link["remote"]))) + # neighbour pings - single subgraphs + for link in links: + remote = get_clean_fieldname(link["remote"]) + print("multigraph olsr_neighbour_ping.host_{remote}".format(remote=remote)) + title = "Ping time of {remote}".format(remote=remote) + print(NEIGHBOUR_PING_CONFIG.format(title=title)) + print(NEIGHBOUR_PING_VALUE.format(host=link["remote"], host_fieldname=remote)) + + +def do_fetch(): + # output values + links = list(get_olsr_links()) + + # overview graph for the link quality (ETX) of all neighbours + print("multigraph olsr_link_quality") + for link in links: + print("lq_{remote}.value {lq:f}".format(lq=link["lq"], + remote=get_clean_fieldname(link["remote"]))) + print("nlq_{remote}.value {nlq:f}".format(nlq=link["nlq"], + remote=get_clean_fieldname(link["remote"]))) + # detailed ETX graph for each single neighbour link + for link in links: + print("multigraph olsr_link_quality.host_{remote}" + .format(remote=get_clean_fieldname(link["remote"]))) + print("lq.value {lq:f}".format(lq=link["lq"])) + print("nlq.value {nlq:f}".format(nlq=link["nlq"])) + + # count the links/nodes behind each neighbour node + print("multigraph olsr_neighbour_link_count") + for link in links: + print("neighbour_{host_fieldname}.value {value}" + .format(value=link["route_count"], + host_fieldname=get_clean_fieldname(link["remote"]))) + + # overview of ping roundtrip times + print("multigraph olsr_neighbour_ping") + ping_times = get_ping_times([link["remote"] for link in links]) + for link in links: + ping_time = ping_times.get(link["remote"], None) + value = "{:.4f}".format(ping_time) if ping_time is not None else "U" + print("neighbour_{remote}.value {value}" + .format(value=value, remote=get_clean_fieldname(link["remote"]))) + # single detailed graphs for the ping time of each link + for link in links: + ping_time = ping_times.get(link["remote"], None) + value = "{:.4f}".format(ping_time) if ping_time is not None else "U" + remote = get_clean_fieldname(link["remote"]) + print("multigraph olsr_neighbour_ping.host_{remote}".format(remote=remote)) + print("neighbour_{remote}.value {value}".format(remote=remote, value=value)) + + +if __name__ == "__main__": + # parse arguments + if len(sys.argv) > 1: + if sys.argv[1] == "config": + do_config() + if os.getenv("MUNIN_CAP_DIRTYCONFIG") == "1": + do_fetch() + sys.exit(0) + elif sys.argv[1] == "autoconf": + if os.path.exists(os.getenv('OLSRD_BIN_PATH', '/usr/sbin/olsrd')): + print('yes') + else: + print('no') + sys.exit(0) + elif sys.argv[1] == "version": + print('olsrd Munin plugin, version %s' % plugin_version) + sys.exit(0) + elif sys.argv[1] == "": + # ignore + pass + else: + # unknown argument + sys.stderr.write("Unknown argument{eol}".format(eol=LINESEP)) + sys.exit(1) + + do_fetch() + +# final marker for shell / python hybrid script (see "Interpreter Selection") +EOF = True +EOF diff --git a/plugins/network/qos_ b/plugins/network/qos_ index 3b9bc6fa..06592904 100755 --- a/plugins/network/qos_ +++ b/plugins/network/qos_ @@ -81,7 +81,7 @@ my( $name, $id1, $id2, $type, $rate, $parent); for( my $x = 0; $x < scalar(@class); $x++ ) { if($class[$x] =~ m/^class/i ) { ( $name, $id1, $id2, $type, $parent) = ( $class[$x] =~ m/^class\s+(\w+)\s+(\d+):(\d+)\s+(\w+)\s([^\s]+)/i ); - if($type eq "parent") { + if($type && $type eq "parent") { $x++; ( $rate ) = ( $class[$x] =~ m/Sent\s+(\d+)\s+/i ); $handle = "$name"."${id1}_${id2}"; @@ -106,6 +106,8 @@ if ( exists $ARGV[0] and $ARGV[0] eq 'config' ) { print "graph_data_size $ENV{graph_data_size}\n" if $ENV{graph_data_size}; print "graph_order "; foreach my $key (sort by_handle keys %queues) { + delete $queues{$key} if $key =~ /sfq/i; + next if $key =~ /sfq/i; $haschild = 0; foreach my $key2 (sort by_handle keys %queues) { if($queues{$key}->{id} eq $queues{$key2}->{parent}) { @@ -136,7 +138,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq 'config' ) { } elsif (exists $ENV{"max$queues{$key}->{handle}"}) { print $queues{$key}->{queue},$queues{$key}->{handle}, ".max ",$ENV{"max$queues{$key}->{handle}"},"\n"; } - print $queues{$key}->{queue},$queues{$key}->{handle}, ".type COUNTER\n"; + print $queues{$key}->{queue},$queues{$key}->{handle}, ".type DERIVE\n"; if($count == 0){ print $queues{$key}->{queue},$queues{$key}->{handle}, ".draw AREA\n"; } else { @@ -157,6 +159,7 @@ sub by_handle { } foreach my $key (sort by_handle keys %queues) { + next if $key =~ /sfq/i; $haschild = 0; foreach my $key2 (sort by_handle keys %queues) { if($queues{$key}->{id} eq $queues{$key2}->{parent}) { @@ -168,4 +171,4 @@ foreach my $key (sort by_handle keys %queues) { print $queues{$key}->{queue},$queues{$key}->{handle}, ".value ",$queues{$key}->{sent}, "\n"; } # -# vim:syntax=perl \ No newline at end of file +# vim:syntax=perl diff --git a/plugins/network/radio b/plugins/network/radio index 1c815823..4534506e 100755 --- a/plugins/network/radio +++ b/plugins/network/radio @@ -98,7 +98,7 @@ switch( $argv[1] ) { case "config": echo "graph_title Stream Listeners\n"; - echo "graph_category Network\n"; + echo "graph_category streaming\n"; echo "graph_vlabel listeners\n"; echo "graph_hlabel listeners\n"; echo "graph_args --base 1000 -l 0\n"; diff --git a/plugins/network/sockstat-via-procfs b/plugins/network/sockstat-via-procfs index 4c875aa2..88afe093 100755 --- a/plugins/network/sockstat-via-procfs +++ b/plugins/network/sockstat-via-procfs @@ -20,13 +20,13 @@ fields = ( if len(sys.argv) > 1: arg = sys.argv[1] if arg == 'autoconfig': - print 'yes' + print('yes') sys.exit(0) if arg == 'config': - print 'graph_title sockstat' - print 'graph_category network' + print('graph_title sockstat') + print('graph_category network') for name, label in fields: - print '%s.label %s' % (name, label) + print('%s.label %s' % (name, label)) sys.exit(0) re_num = re.compile('(\d+)') @@ -34,6 +34,6 @@ sockstat = open('/proc/net/sockstat').read() numbers = re_num.findall(sockstat) for i, (name, label) in enumerate(fields): - print '%s.value %s' % (name, numbers[i]) + print('%s.value %s' % (name, numbers[i])) sys.exit(0) diff --git a/plugins/network/tc_ b/plugins/network/tc_ index 1418222d..9933edd2 100755 --- a/plugins/network/tc_ +++ b/plugins/network/tc_ @@ -1,25 +1,54 @@ #!/bin/sh # -*- sh -*- -# -# This plugin is based on the if_ plugin. -# -# Parameters -# None -# -# Magic markers (optional - used by munin-config and some installation -# scripts): -# -#%# family=manual -#%# capabilities=autoconf suggest +: << =cut + +=pod + +=encoding UTF-8 + +=head1 NAME + +tc_ - Plugin to monitor traffic control queue class bandwidth usage + +=head1 CONFIGURATION + +None needed. + +=head1 INTERPRETATION + +Traffic control is the name given to the sets of queuing systems and mechanisms by which packets are received and transmitted on a router. This includes deciding which (and whether) packets to accept at what rate on the input of an interface and determining which packets to transmit in what order at what rate on the output of an interface. + +This plugin monitors the bandwidth used by each queue class. The root class will draw as a single line, whereas children will be drawn in a stacked graph. Complex hierarchies which have more than the root and its direct children are not fully supported and may not render correctly. + +=head1 SEE ALSO + +"man tc" and "tc -s class show dev " to get more information about tc and to see the format of the statistics being parsed. + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf suggest + +=head1 AUTHORS + +Steve Schnepp , +Samuel Smith , +Nye Liu + +=head1 LICENSE + +GPLv2 or later + +=cut DEVICE=${0##*/tc_} mytc() { - tc -s class show dev $1 | tr "\n" "|" | sed "s/ \+/ /g" | sed "s/ |/|/g" | sed "s/| /|/g" | sed "s/||/\n/g" | sed "s/|/ /g" | tr ":" "_" | sort -n + /sbin/tc -s class show dev "$1" | tr "\n" "|" | sed -e "s/ \+/ /g; s/ |/|/g; s/| /|/g; s/||/\n/g; s/|/ /g" | tr ":" "_" | grep -v -i sfq | sort -n } -case $1 in +case "$1" in autoconf) if [ -r /proc/net/dev ]; then echo yes @@ -36,29 +65,47 @@ case $1 in split($0, a, /: */); gsub(/^ +/,"",a[1]); if (($2 > 0) || ($10 > 0)) print a[1]; }' /proc/net/dev - -# egrep '^ *(eth|tap|bond|wlan|ath|ra|sw)[0-9]+:' /proc/net/dev | cut -f1 -d: | sed 's/ //g' fi exit 0 ;; config) - - echo "graph_order `mytc $DEVICE | awk '{ print $2 "_" $3 }' | sort -n | tr "\n" " "`" + echo "graph_order $(mytc "$DEVICE" | awk '{ print $2 "_" $3 }' | tr "\n" " ")" echo "graph_title $DEVICE TC traffic" - echo 'graph_args --base 1000' - echo 'graph_vlabel bits per ${graph_period}' + echo 'graph_args --base 1000' + echo 'graph_vlabel bits per ${graph_period}' echo 'graph_category network' echo "graph_info This graph shows the TC classes traffic of the $DEVICE network interface. Please note that the traffic is shown in bits per second, not bytes." - - mytc $DEVICE | tr "_" " " | awk '{ print $2 "_" $3 "_" $4 ".label " $2 "/" $3 ":" $4 "\n" $2 "_" $3 "_" $4 ".type COUNTER\n" $2 "_" $3 "_" $4 ".min 0\n" $2 "_" $3 "_" $4 ".cdef " $2 "_" $3 "_" $4 ",8,*" }' + + # the root(s) + mytc "$DEVICE" | grep -v " parent " | tr "_" " " | awk '{ + print $2 "_" $3 "_" $4 ".label " $2 "/" $3 ":" $4; + print $2 "_" $3 "_" $4 ".type DERIVE"; + print $2 "_" $3 "_" $4 ".min 0"; + print $2 "_" $3 "_" $4 ".cdef " $2 "_" $3 "_" $4 ",8,*"; + }' + # TODO: only AREASTACK things with no children + # the child(s) + mytc "$DEVICE" | grep " parent " | tr "_" " " | awk '{ + print $2 "_" $3 "_" $4 ".label " $2 "/" $3 ":" $4; + print $2 "_" $3 "_" $4 ".type DERIVE"; + print $2 "_" $3 "_" $4 ".min 0"; + print $2 "_" $3 "_" $4 ".cdef " $2 "_" $3 "_" $4 ",8,*"; + print $2 "_" $3 "_" $4 ".draw AREASTACK"; + }' exit 0 ;; esac # the root(s) -mytc $DEVICE | grep -v " parent " | awk "{ print \$2 \"_\" \$3 \".value \" \$14}" +mytc "$DEVICE" | grep -v " parent " | awk '{ + split(substr($0, match($0, /[0-9]+ [Bb]ytes/)), a, " "); + print $2 "_" $3 ".value " a[1]; +}' # the child(s) -mytc $DEVICE | grep " parent " | awk "{ print \$2 \"_\" \$3 \".value \" \$19}" +mytc "$DEVICE" | grep " parent " | awk '{ + split(substr($0, match($0, /[0-9]+ [Bb]ytes/)), a, " "); + print $2 "_" $3 ".value " a[1]; +}' exit 0 diff --git a/plugins/network/tc_drops_ b/plugins/network/tc_drops_ index fc89ab38..f719291b 100755 --- a/plugins/network/tc_drops_ +++ b/plugins/network/tc_drops_ @@ -16,7 +16,7 @@ DEVICE=${0##*/tc_drops_} mytc() { - tc -s class show dev $1 | tr "\n," "| " | sed "s/ \+/ /g" | sed "s/ |/|/g" | sed "s/| /|/g" | sed "s/||/\n/g" | sed "s/|/ /g" | tr ":" "_" | sort -n + tc -s class show dev $1 | tr "\n," "| " | sed "s/ \+/ /g" | sed "s/ |/|/g" | sed "s/| /|/g" | sed "s/||/\n/g" | sed "s/|/ /g" | tr ":" "_" | grep -v -i sfq | sort -n } case $1 in @@ -43,15 +43,15 @@ case $1 in ;; config) - echo "graph_order `mytc $DEVICE | awk '{ print $2 "_" $3 "_drops" }' | sort -n | tr "\n" " "`" + echo "graph_order `mytc $DEVICE | awk '{ print $2 "_" $3 "_drops" }' | tr "\n" " "`" echo "graph_title $DEVICE TC traffic drops" echo 'graph_args --base 1000' - echo 'graph_vlabel bits per ${graph_period}' + echo 'graph_vlabel drops per ${graph_period}' echo 'graph_category network' echo "graph_info This graph shows the TC classes traffic drops of the $DEVICE network interface, epxressed in packets." # mytc $DEVICE | tr "_" " " | awk '{ print $2 "_" $3 "_" $4 "_drops.label " $2 "/" $3 ":" $4 "\n" $2 "_" $3 "_" $4 "_drops.type COUNTER\n" $2 "_" $3 "_" $4 "_drops.min 0\n" $2 "_" $3 "_" $4 "_drops.cdef " $2 "_" $3 "_" $4 ",8,*" }' - mytc $DEVICE | tr "_" " " | awk '{ print $2 "_" $3 "_" $4 "_drops.label " $2 "/" $3 ":" $4 "\n" $2 "_" $3 "_" $4 "_drops.type COUNTER\n" $2 "_" $3 "_" $4 "_drops.min 0" }' + mytc $DEVICE | tr "_" " " | awk '{ print $2 "_" $3 "_" $4 "_drops.label " $2 "/" $3 ":" $4 "\n" $2 "_" $3 "_" $4 "_drops.type DERIVE\n" $2 "_" $3 "_" $4 "_drops.min 0" }' exit 0 ;; esac diff --git a/plugins/network/tc_packets_ b/plugins/network/tc_packets_ index 99382325..8ae58204 100755 --- a/plugins/network/tc_packets_ +++ b/plugins/network/tc_packets_ @@ -16,7 +16,7 @@ DEVICE=${0##*/tc_packets_} mytc() { - tc -s class show dev $1 | tr "\n," "| " | sed "s/ \+/ /g" | sed "s/ |/|/g" | sed "s/| /|/g" | sed "s/||/\n/g" | sed "s/|/ /g" | tr ":" "_" | sort -n + tc -s class show dev $1 | tr "\n," "| " | sed "s/ \+/ /g" | sed "s/ |/|/g" | sed "s/| /|/g" | sed "s/||/\n/g" | sed "s/|/ /g" | tr ":" "_" | grep -v -i sfq | sort -n } case $1 in @@ -43,15 +43,15 @@ case $1 in ;; config) - echo "graph_order `mytc $DEVICE | awk '{ print $2 "_" $3 "_packets" }' | sort -n | tr "\n" " "`" + echo "graph_order `mytc $DEVICE | awk '{ print $2 "_" $3 "_packets" }' | tr "\n" " "`" echo "graph_title $DEVICE TC traffic packets" echo 'graph_args --base 1000' - echo 'graph_vlabel bits per ${graph_period}' + echo 'graph_vlabel packets per ${graph_period}' echo 'graph_category network' echo "graph_info This graph shows the TC classes traffic packets of the $DEVICE network interface." # mytc $DEVICE | tr "_" " " | awk '{ print $2 "_" $3 "_" $4 "_packets.label " $2 "/" $3 ":" $4 "\n" $2 "_" $3 "_" $4 "_packets.type COUNTER\n" $2 "_" $3 "_" $4 "_packets.min 0\n" $2 "_" $3 "_" $4 "_packets.cdef " $2 "_" $3 "_" $4 ",8,*" }' - mytc $DEVICE | tr "_" " " | awk '{ print $2 "_" $3 "_" $4 "_packets.label " $2 "/" $3 ":" $4 "\n" $2 "_" $3 "_" $4 "_packets.type COUNTER\n" $2 "_" $3 "_" $4 "_packets.min 0" }' + mytc $DEVICE | tr "_" " " | awk '{ print $2 "_" $3 "_" $4 "_packets.label " $2 "/" $3 ":" $4 "\n" $2 "_" $3 "_" $4 "_packets.type DERIVE\n" $2 "_" $3 "_" $4 "_packets.min 0" }' exit 0 ;; esac diff --git a/plugins/network/traffic_ipt b/plugins/network/traffic_ipt index a2a994e4..29f899f5 100755 --- a/plugins/network/traffic_ipt +++ b/plugins/network/traffic_ipt @@ -110,7 +110,7 @@ ipv6=0 diffv4=0 diffv6=0 -IPv4_bytes=$(iptables -L -n -v -x | egrep '^\W*[0-9]+\W+[0-9]+\W+all\W+--\W+\*\W+\*\W+0.0.0.0/0\W+0.0.0.0/0\W*$' | while read pkts bytes rest; do echo $bytes; done) +IPv4_bytes=$(iptables -L -n -v -x -w | egrep '^\W*[0-9]+\W+[0-9]+\W+all\W+--\W+\*\W+\*\W+0.0.0.0/0\W+0.0.0.0/0\W*$' | while read pkts bytes rest; do echo $bytes; done) if [ -z "$IPv4_bytes" ]; then echo "W: Unable to read rule from iptables, please add rules" >&2 @@ -118,7 +118,7 @@ else ipv4=$(echo $IPv4_bytes | sed -e 's/ / + /' | bc -l) fi -IPv6_bytes=$(ip6tables -L -n -v -x | egrep '^\W*[0-9]+\W+[0-9]+\W+all\W+\*\W+\*\W+::/0\W+::/0\W*$' | while read pkts bytes rest; do echo $bytes; done) +IPv6_bytes=$(ip6tables -L -n -v -x -w | egrep '^\W*[0-9]+\W+[0-9]+\W+all\W+\*\W+\*\W+::/0\W+::/0\W*$' | while read pkts bytes rest; do echo $bytes; done) if [ -z "$IPv6_bytes" ]; then echo "W: Unable to read rule from ip6tables, please add rules" >&2 diff --git a/plugins/network/transmission_ratios/tr_ratios b/plugins/network/transmission_ratios/tr_ratios deleted file mode 100755 index ec2daae9..00000000 --- a/plugins/network/transmission_ratios/tr_ratios +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -user='' -pass='' - -if [ "$1" = "config" ]; then - echo "graph_title Transmission seed ratios" - echo "graph_vlabel Seed ratio %" - echo "graph_category torrent" - echo "graph_info This plugin shows your transmission ratios per torrent" - transmission-remote -n $user:$pass -l | gawk -f /usr/share/munin/plugins/tr_ratios_labels - exit 0 -fi - -transmission-remote -n $user:$pass -l | gawk -f /usr/share/munin/plugins/tr_ratios_data - diff --git a/plugins/network/transmission_ratios/tr_ratios_data b/plugins/network/transmission_ratios/tr_ratios_data deleted file mode 100644 index fd9dd551..00000000 --- a/plugins/network/transmission_ratios/tr_ratios_data +++ /dev/null @@ -1,10 +0,0 @@ -BEGIN { FIELDWIDTHS = "7 4 13 10 7 9 7 13 40" } -NR > 1 { - split($1,torrentid," ") - if(torrentid[1] != "Sum:") { - split($7,ratio," ") - ratio[1] = ratio[1] * 100 - print "ID" torrentid[1] ".value " ratio[1] - } -} - diff --git a/plugins/network/transmission_ratios/tr_ratios_labels b/plugins/network/transmission_ratios/tr_ratios_labels deleted file mode 100644 index 273ab3d3..00000000 --- a/plugins/network/transmission_ratios/tr_ratios_labels +++ /dev/null @@ -1,8 +0,0 @@ -BEGIN { FIELDWIDTHS = "7 4 13 10 7 9 7 13 40" } -NR > 1 { - split($1,torrentid," ") - if(torrentid[1] != "Sum:") { - print "ID" torrentid[1] ".label " $9 - } -} - diff --git a/plugins/network/ubiquiti_airfiber_ b/plugins/network/ubiquiti_airfiber_ old mode 100644 new mode 100755 diff --git a/plugins/network/ubiquiti_airos_ b/plugins/network/ubiquiti_airos_ old mode 100644 new mode 100755 diff --git a/plugins/network/udp-statistics b/plugins/network/udp-statistics deleted file mode 100755 index 5a3e78a8..00000000 --- a/plugins/network/udp-statistics +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/perl -w - -if ( $ARGV[0] ) { - - if ( $ARGV[0] eq 'autoconf' ) { - if ( -r '/bin/netstat') { - print "yes\n"; - exit 0; - } - print "no\n"; - exit 0; - - } elsif ( $ARGV[0] eq 'config' ) { - print < + +=head1 LICENSE + +GPLv2 + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf suggest + +=cut + +autoconf() { + which upnpc >/dev/null && upnpc -s >/dev/null 2>&1 && echo yes || echo "no (No upnpc or no UPnP router)" +} + +suggest () { + upnpc -s | sed -n " \ + s/.*uptime=.*/uptime/p; \ + s/.*MaxBitRate.*/bitrate/p; \ + s/.*Bytes.*/traffic/p; \ + s/.*Packets.*/pkts/p; \ + " +} + +config () { + case $1 in + "uptime") + cat << EOF +graph_title Uplink connection uptime +graph_args -l 0 +graph_category network +graph_scale no +graph_vlabel uptime in hours +uptime.label uptime +uptime.draw AREA +uptime.cdef uptime,3600,/ +EOF + ;; + "bitrate") + cat << EOF +graph_title Uplink bitrate +graph_args --base 1000 -l 0 +graph_category network +graph_vlabel bitrate down (-) / up (+) +down.label bps +down.warning 4194304: +down.critical 1048576: +up.label bps +up.warning 524288: +up.critical 131072: +down.graph no +up.negative down +EOF + ;; + "traffic") + cat << EOF +graph_title Uplink traffic +graph_args --base 1024 -l 0 +graph_category network +graph_vlabel bytes in (-) / out (+) per ${graph_period} +down.label Bps +down.type DERIVE +down.min 0 +up.label Bps +up.type DERIVE +up.min 0 +down.graph no +up.negative down +EOF + ;; + "pkts") + cat << EOF +graph_title Uplink packets +graph_args --base 1000 -l 0 +graph_category network +graph_vlabel packets in (-) / out (+) per ${graph_period} +down.label pps +down.type DERIVE +down.min 0 +up.label pps +up.type DERIVE +up.min 0 +down.graph no +up.negative down +EOF + ;; + "*") + echo "$0: unknown mode '$1'" >&2 + exit 1 + esac +} + +fetch () { + case $1 in + "uptime") + upnpc -s | sed -n "s/.*uptime=\([0-9]\+\)s.*/uptime.value \1/p" + ;; + "bitrate") + upnpc -s | sed -n "s/^MaxBitRateDown : \([0-9]\+\) bps.*MaxBitRateUp \([0-9]\+\) bps.*/down.value \1\nup.value \2/p" + ;; + "traffic") + upnpc -s | sed -n "s/^Bytes:\s*Sent:\s*\([0-9]\+\).*Recv:\s*\([0-9]\+\).*/up.value \1\ndown.value \2/p" + ;; + "pkts") + upnpc -s | sed -n "s/^Packets:\s*Sent:\s*\([0-9]\+\).*Recv:\s*\([0-9]\+\).*/up.value \1\ndown.value \2/p" + ;; + "*") + echo "$0: unknown mode '$1'" >&2 + exit 1 + esac +} + +mode=`echo $0 | sed 's/.*_//'` + +case $1 in + "autoconf") + autoconf + ;; + "suggest") + suggest + ;; + "config") + config $mode + ;; + *) + fetch $mode + ;; +esac diff --git a/plugins/network/vnstat_month b/plugins/network/vnstat_month index 8bd15620..6abc7ee2 100755 --- a/plugins/network/vnstat_month +++ b/plugins/network/vnstat_month @@ -4,6 +4,7 @@ use strict; use warnings; use autodie; use List::Util 'sum'; +use experimental 'switch'; =head1 NAME diff --git a/plugins/network/zenus_ b/plugins/network/zenus_ index 00e2a5b5..319bf2fd 100755 --- a/plugins/network/zenus_ +++ b/plugins/network/zenus_ @@ -66,6 +66,14 @@ if ( !eval "require zenus;" ) { $ret .= "(BTW, \@INC is: " . join( ', ', @INC ) . ")\n"; } +my $CAN_LOG = 1; +if ( !eval "require Log::Log4perl;" ) { + $CAN_LOG = 0; +} +elsif ( !eval "require Log::Dispatch::FileRotate;" ) { + $CAN_LOG = 0; +} + my $USER = $ENV{user}; my $PASS = $ENV{pass}; my $ACCT = $ENV{account} || ""; @@ -85,15 +93,33 @@ if ( scalar @name_fields == 3 ) { # If there are more or less than 3 components to the filename, # we just carry on with the default account +if ($CAN_LOG) { + my $loggerconf = q( + log4perl.rootLogger = DEBUG, LOG1 + log4perl.appender.LOG1 = Log::Dispatch::FileRotate + log4perl.appender.LOG1.filename = /var/log/munin/zenus.log + log4perl.appender.LOG1.mode = append + log4perl.appender.LOG1.DatePattern = yyyy-ww + log4perl.appender.LOG1.layout = Log::Log4perl::Layout::PatternLayout + log4perl.appender.LOG1.layout.ConversionPattern = %d %p %c: %m%n + ); + Log::Log4perl::init( \$loggerconf ); + our $logger = Log::Log4perl->get_logger($ACCT); +} else { + our $logger = ""; +} + my $lastread; sub save_data { my $hashref = shift; + my $update_lastread = shift || 1; # Do we need to save this info - if ( time > $lastread + ( $TICK * 60 ) ) { + if ( !defined $lastread or time > $lastread + ( $TICK * 60 ) ) { - $lastread = time; + $lastread = time if $update_lastread; + print "# Updating LastRead to " . localtime($lastread) . "\n"; my @save_vector; push @save_vector, $lastread; @@ -103,6 +129,10 @@ sub save_data { push @save_vector, $_ . '' . $hashref->{$_}; } + $::logger->info( + "Saving Data (LastRead is " . localtime($lastread) . "\n" ) + if $CAN_LOG; + #Go! save_state(@save_vector); } @@ -121,11 +151,50 @@ sub load_data { my ( $key, $value ) = split //; $hashref->{$key} = $value; } + my $force_save = 0; if ( !defined $lastread or time >= ( $lastread + ( $TICK * 60 ) ) ) { # Data is stale + print "# Data is " . ( time - $lastread ) . " seconds old\n"; + $::logger->info( "Data is " . ( time - $lastread ) . " seconds old\n" ) + if $CAN_LOG; - #print STDERR "REFRESHING DATA\n"; + if ( exists $hashref->{backoff} ) { + if ( time <= $hashref->{backoff} ) { + + # We're in a back-off period + print "# Back-off in effect\n"; + $::logger->info("Back-off in effect\n") if $CAN_LOG; + exit 0; + } + else { + # We've just come out of a back-off period + print "# Back-off ends\n"; + $::logger->info("Back-off ends\n") if $CAN_LOG; + delete $hashref->{backoff}; + } + } + elsif ( !defined $lastread ) { + + # Initial run. Carry on + print "# Initial Run\n"; + my $force_save = 1; + } + elsif ( time >= ( $lastread + ( 120 * 60 ) ) ) { + + # Data is VERY Stale. Assume a login issue and + # back-off for 24 hours. + print "# Starting Back-Off\n"; + $hashref->{backoff} = time + ( 24 * 60 * 60 ); + $::logger->info( + "Backing off until " . localtime( $hashref->{backoff} ) . "\n" ) + if $CAN_LOG; + save_data( $hashref, 0 ); + exit 1; + } + + print "# REFRESHING DATA\n"; + $::logger->info("Refreshing Data\n") if $CAN_LOG; my $temphash; eval { zenus::login( $USER, $PASS ); @@ -144,11 +213,19 @@ sub load_data { = zenus::estimateRemaining( $temphash->{used}, $temphash->{avail} ); $hashref = $temphash; + 1; # If zenus threw an error we won't copy the data over, # so we still use the cached data. + } or do { + print "# Zenus Error $@\n"; }; } + else { + print "# Using existing data\n"; + $::logger->info("Using existing data\n") if $CAN_LOG; + } + save_data( $hashref, 1 ) if $force_save; return $hashref; } @@ -181,15 +258,12 @@ if ( defined $ARGV[0] and $ARGV[0] eq "config" ) { exit 1; } my $data = load_data(); - my $cap = sprintf( "%.3f", $data->{avail} ); - my $warn = sprintf( "%.3f", $cap * 0.25 ); - my $crit = sprintf( "%.3f", $cap * 0.1 ); my $datestr = scalar( localtime($lastread) ); print <{name} upload.label Uploaded upload.type GAUGE @@ -199,11 +273,17 @@ upload.graph no upload.colour 00CC00EE download.label Transfer download.type GAUGE -download.info Amount of data downloaded (Limit is $cap GB) download.draw AREA download.extinfo Last Read was $datestr download.negative upload download.colour 00CC00EE +EOF + if ( defined $data->{avail} and $data->{avail} != 0 ) { + my $cap = sprintf( "%.3f", $data->{avail} ); + my $warn = sprintf( "%.3f", $cap * 0.25 ); + my $crit = sprintf( "%.3f", $cap * 0.1 ); + print <{uploadAmount} . "\n"; print "download.value " . $data->{used} . "\n"; -print "allowance.value " . $data->{avail} . "\n"; -my $remain = $data->{avail} - $data->{estusage}; -$remain = 0 if $remain < 0; -print "remaining.value " . $remain . "\n"; -my $overrate = $data->{avedaily} - $data->{maxdaily}; -$overrate = 0 if $overrate < 0; -print "overrate.value " . $overrate . "\n"; +if ( defined $data->{avail} and $data->{avail} != 0 ) { + print "allowance.value " . $data->{avail} . "\n"; + my $remain = $data->{avail} - $data->{estusage}; + $remain = 0 if $remain < 0; + print "remaining.value " . $remain . "\n"; + my $overrate = $data->{avedaily} - $data->{maxdaily}; + $overrate = 0 if $overrate < 0; + print "overrate.value " . $overrate . "\n"; +} save_data($data); exit 0; diff --git a/plugins/newznab/nn_ b/plugins/newznab/nn_ index 45794174..0bb1c601 100755 --- a/plugins/newznab/nn_ +++ b/plugins/newznab/nn_ @@ -218,7 +218,7 @@ sub config { while (my ($k, $v) = each %conf) { print "graph_$k $v\n"; } - print "graph_category newznab\n"; + print "graph_category search\n"; my $i = 0; for my $ds (@{$graph->{data_sources}}) { diff --git a/plugins/nfs-freebsd/nfs_client b/plugins/nfs-freebsd/nfs_client old mode 100644 new mode 100755 index 197c3d86..312fb6f2 --- a/plugins/nfs-freebsd/nfs_client +++ b/plugins/nfs-freebsd/nfs_client @@ -48,7 +48,7 @@ if [ "$1" = "config" ]; then echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel requests / ${graph_period}' echo 'graph_total total' - echo 'graph_category NFS' + echo 'graph_category fs' for a in $labels; do echo "$a.label $a" ; echo "$a.type DERIVE"; echo "$a.min 0"; done exit 0 fi diff --git a/plugins/nfs-freebsd/nfs_client_cache b/plugins/nfs-freebsd/nfs_client_cache old mode 100644 new mode 100755 index c29043b0..9c5d3441 --- a/plugins/nfs-freebsd/nfs_client_cache +++ b/plugins/nfs-freebsd/nfs_client_cache @@ -48,7 +48,7 @@ if [ "$1" = "config" ]; then echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel requests / ${graph_period}' echo 'graph_total total' - echo 'graph_category NFS' + echo 'graph_category fs' for a in $labels; do echo "$a.label $a" ; echo "$a.type DERIVE"; echo "$a.min 0"; done exit 0 fi diff --git a/plugins/nfs-freebsd/nfsd b/plugins/nfs-freebsd/nfsd index 344679ac..d551b844 100755 --- a/plugins/nfs-freebsd/nfsd +++ b/plugins/nfs-freebsd/nfsd @@ -50,7 +50,7 @@ if [ "$1" = "config" ]; then echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel requests / ${graph_period}' echo 'graph_total total' - echo 'graph_category NFS' + echo 'graph_category fs' for a in $labels; do echo "$a.label $a" ; echo "$a.type DERIVE"; echo "$a.min 0"; done exit 0 fi diff --git a/plugins/network/nfsv4 b/plugins/nfs/nfsv4 similarity index 98% rename from plugins/network/nfsv4 rename to plugins/nfs/nfsv4 index 4eb8e563..1de3e7e0 100755 --- a/plugins/network/nfsv4 +++ b/plugins/nfs/nfsv4 @@ -61,7 +61,7 @@ if [ "$1" = "config" ]; then echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel requests / ${graph_period}' echo 'graph_total total' - echo 'graph_category NFS' + echo 'graph_category fs' for a in $proc ; do echo "$a.label $a" ; echo "$a.type DERIVE"; echo "$a.min 0"; done exit 0 fi diff --git a/plugins/nginx/example-graphs/nginx_connection_request-month.png b/plugins/nginx/example-graphs/nginx_connection_request-month.png new file mode 100644 index 00000000..ad22d39a Binary files /dev/null and b/plugins/nginx/example-graphs/nginx_connection_request-month.png differ diff --git a/plugins/nginx/nginx-cache-hit-rate b/plugins/nginx/nginx-cache-hit-rate index a241a8c4..ade87fe7 100755 --- a/plugins/nginx/nginx-cache-hit-rate +++ b/plugins/nginx/nginx-cache-hit-rate @@ -57,7 +57,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "autoconf" ) { if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { print "graph_title NGINX hit rates\n"; print "graph_args -l 0 -u 100 --rigid\n"; - print "graph_category nginx\n"; + print "graph_category webserver\n"; print "graph_vlabel %\n"; print "hit.label Hits\n"; print "hit.draw AREA\n"; diff --git a/plugins/nginx/nginx-cache-multi_ b/plugins/nginx/nginx-cache-multi_ index 3ab8046b..97e9f25f 100755 --- a/plugins/nginx/nginx-cache-multi_ +++ b/plugins/nginx/nginx-cache-multi_ @@ -11,7 +11,7 @@ # This plugin generates two graphs - with number of requests and with percents. # Create these symlinks: # ln -s /usr/share/munin/plugins/nginx-cache-multi_ /etc/munin/plugins/nginx-cache-multi_number -# ls -s /usr/share/munin/plugins/nginx-cache-multi_ /etc/munin/plugins/nginx-cache-multi_percent +# ln -s /usr/share/munin/plugins/nginx-cache-multi_ /etc/munin/plugins/nginx-cache-multi_percent # # You can override the log file location. # @@ -64,7 +64,7 @@ elif len(sys.argv) == 2 and sys.argv[1] == "config": # Parent graph declaration print "multigraph nginx_cache_multi_%s" % graph_type print "graph_title Nginx cache status (%s)" % graph_type - print "graph_category nginx" + print "graph_category webserver" print "graph_vlabel %s" % vlabel if graph_args: print(graph_args) @@ -80,7 +80,7 @@ elif len(sys.argv) == 2 and sys.argv[1] == "config": if key != "TOTAL": print "multigraph nginx_cache_multi_%s.%s" % (graph_type, key.lower()) print "graph_title Nginx cache status (%s) - %s" % (graph_type, key.capitalize()) - print "graph_category nginx" + print "graph_category webserver" print "graph_vlabel %s" % vlabel print "%s.label %s" % (key.lower(), key.capitalize()) print "%s.draw LINE1" % key.lower() diff --git a/plugins/nginx/nginx-combined b/plugins/nginx/nginx-combined index a8052a3c..0adf8362 100755 --- a/plugins/nginx/nginx-combined +++ b/plugins/nginx/nginx-combined @@ -70,7 +70,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { print "graph_title NGINX status: $URL\n"; print "graph_args --base 1000\n"; - print "graph_category nginx\n"; + print "graph_category webserver\n"; print "graph_vlabel Connections\n"; print "reqpsec.label Request/sec.\n"; diff --git a/plugins/nginx/nginx_byprojects/README.md b/plugins/nginx/nginx_byprojects/README.md index 6dbb4840..7f93095f 100644 --- a/plugins/nginx/nginx_byprojects/README.md +++ b/plugins/nginx/nginx_byprojects/README.md @@ -6,36 +6,27 @@ Count the number of hits per projects/vhost. ![byproject_access](https://www.mantor.org/~northox/misc/munin-plugins/nginx_byprojects_access1-month.png "byproject_access") ## munin_byprojects_bandwidth -Count the total bandwidth used by each projects/vhost. [Logtail] (https://www.fourmilab.ch/webtools/logtail/) is required. +Count the total bandwidth used by each projects/vhost. [Logtail](https://www.fourmilab.ch/webtools/logtail/) is required. ![byproject_bandwidth](https://www.mantor.org/~northox/misc/munin-plugins/apache_byprojects_bandwidth-month.png "byproject_bandwidth") ## munin_byprojects_inout_bandwidth -Counts the in/out bandwidth used by each projects/vhost. [Logtail] (https://www.fourmilab.ch/webtools/logtail/) is required. +Counts the in/out bandwidth used by each projects/vhost. [Logtail](https://www.fourmilab.ch/webtools/logtail/) is required. ![byproject_inout_bandwidth](https://www.mantor.org/~northox/misc/munin-plugins/apache_byprojects_inout_bandwidth-month.png "byproject_inout_bandwidth") ## Installation The setup is pretty straight forward. First you need to configure the plugin: -Define the file which will be used by logtail to identify it's position in the log and the path to logtail: +In your munin plugin configuration file (for example, a new dedicated /etc/munin/plugin-conf.d/nginx_byprojects), configure the plugins : - $statepath = '/usr/local/var/munin/plugin-state'; # directory where logtail will save the state - $logtail = '/usr/local/bin/logtail'; + [byprojects_*] + env.logtail /usr/local/bin/logtail -Multiple logs can be used for the same project/vhost and a regular expression (regex) can be used as a filter: +Multiple logs can be used for the same project/vhost and a regular expression (regex) can be used as a filter. +Each log is defined in a dedicated environment variable, named env.site.[siteName]. The value is JSON formatted. - my %logs = ( - 'prod' => [ - {'path' => '/home/prod/log/access.log'} - ], - 'dev' => [ - {'path' => '/var/log/httpd/ssl-dev-access.log'}, - {'path' => '/home/dev/log/access*.log'} # glob is supported - ], - 'test' => [ - {'path' => '/var/log/access.log', 'regex' => '"[A-Z]+ /test/'}, - {'path' => '/home/test/log/access.log'} - ], - ); + env.site.prod [{"path":"/home/prod/log/access.log"}] + env.site.dev [{"path":"/var/log/httpd/ssl-dev-access.log"}, {"path":"/home/dev/log/access*.log"}] + env.site.test [{"path":"/var/log/access.log","regex":"\"[A-Z]+ /test/"}, {"path":"/home/test/log/access.log"}] In the previous example the prod project graph will be using everything in /home/prod/log/access.log. The test project will be using eveything in /home/test/log/access.log and stuff that match '"[A-Z] /test/' in /var/log/httpd/access.log (e.g. "GET /test/). @@ -43,5 +34,7 @@ Then link the file just as any other plugins. ln -s /usr/local/sbin/ /usr/local/etc/munin/plugins/ +And restart the munin-node service. + ## License MIT diff --git a/plugins/nginx/nginx_byprojects/byprojects_access b/plugins/nginx/nginx_byprojects/byprojects_access old mode 100644 new mode 100755 index 782dfc00..22572bf3 --- a/plugins/nginx/nginx_byprojects/byprojects_access +++ b/plugins/nginx/nginx_byprojects/byprojects_access @@ -1,5 +1,6 @@ #!/usr/bin/perl -w use strict; +use JSON qw(decode_json); # # byprojects_access # @@ -23,25 +24,23 @@ use strict; # Test graph will be using everything file matching /home/test/log/access*.log # and stuff that match the expression '"[A-Z] /test/' in /var/log/access.log # such as '"GET /test/' +# +# Configuration +# [byprojects_*] +# env.logtail_path /usr/local/bin/logtail +# env.site.prod [{"path":"/home/prod/log/access.log"}] +# env.site.dev [{"path":"/var/log/httpd/ssl-dev-access.log"}, {"path":"/home/dev/log/access*.log"}] +# env.site.test [{"path":"/var/log/access.log","regex":"\"[A-Z]+ /test/"}, {"path":"/home/test/log/access.log"}] my $server = 'Nginx'; -my $statepath = '/usr/local/var/munin/plugin-state'; -my $logtail = '/usr/local/bin/logtail'; +my $statepath = $ENV{MUNIN_PLUGSTATE}; +my $logtail = $ENV{logtail_path} || '/usr/local/bin/logtail'; -my %logs = ( - 'prod' => [ - {'path' => '/home/prod/log/access.log'} - ], - 'dev' => [ - {'path' => '/var/log/httpd/ssl-dev-access.log'}, - {'path' => '/home/dev/log/access*.log'} # glob is supported - ], - 'test' => [ - {'path' => '/var/log/access.log', 'regex' => '"[A-Z]+ /test/'}, - {'path' => '/home/test/log/access.log'} - ], -); +my @loglist = grep {$_ =~ /site\./} keys(%ENV); +my %envLogs = $ENV{@loglist}; +my %logs; +while(my($k, $v) = each %envLogs) { @logs{substr($k, 5)} = decode_json($v); } ########### @@ -56,7 +55,7 @@ if(defined($ARGV[0])) { print "graph_title $server access byprojects\n"; print "graph_total Total\n"; print "graph_vlabel Access by \${graph_period}\n"; - print "graph_category $server\n"; + print "graph_category webserver\n"; print "graph_info This graph show $server access by various projects.\n"; while ((my $project, my @files) = each(%logs)) { print $project.".label $project\n"; diff --git a/plugins/nginx/nginx_byprojects/byprojects_bandwidth b/plugins/nginx/nginx_byprojects/byprojects_bandwidth old mode 100644 new mode 100755 index 62126994..42a036ec --- a/plugins/nginx/nginx_byprojects/byprojects_bandwidth +++ b/plugins/nginx/nginx_byprojects/byprojects_bandwidth @@ -1,5 +1,6 @@ #!/usr/bin/perl -w use strict; +use JSON qw(decode_json); # # byprojects_bandwidth # @@ -29,26 +30,23 @@ use strict; # Test graph will be using everything file matching /home/test/log/access*.log # and stuff that match the expression '"[A-Z] /test/' in /var/log/access.log # such as '"GET /test/' +# +# Configuration +# [byprojects_*] +# env.logtail_path /usr/local/bin/logtail +# env.site.prod [{"path":"/home/prod/log/access.log"}] +# env.site.dev [{"path":"/var/log/httpd/ssl-dev-access.log"}, {"path":"/home/dev/log/access*.log"}] +# env.site.test [{"path":"/var/log/access.log","regex":"\"[A-Z]+ /test/"}, {"path":"/home/test/log/access.log"}] my $server = 'Nginx'; -my $statepath = '/usr/local/var/munin/plugin-state'; -my $logtail = '/usr/local/bin/logtail'; - -my %logs = ( - 'prod' => [ - {'path' => '/home/prod/log/access.log'} - ], - 'dev' => [ - {'path' => '/var/log/httpd/ssl-dev-access.log'}, - {'path' => '/home/dev/log/access.log'} - ], - 'test' => [ - {'path' => '/var/log/access.log', 'regex' => '"[A-Z]+ /test/'}, - {'path' => '/home/test/log/access.log'} - ], -); +my $statepath = $ENV{MUNIN_PLUGSTATE}; +my $logtail = $ENV{logtail_path} || '/usr/local/bin/logtail'; +my @loglist = grep {$_ =~ /site\./} keys(%ENV); +my %envLogs = $ENV{@loglist}; +my %logs; +while(my($k, $v) = each %envLogs) { @logs{substr($k, 5)} = decode_json($v); } ########### @@ -63,7 +61,7 @@ if(defined($ARGV[0])) { print "graph_title $server total bandwidth byprojects\n"; print "graph_total Total\n"; print "graph_vlabel Bits\n"; - print "graph_category $server\n"; + print "graph_category webserver\n"; print "graph_info This graph show $server total bandwidth used by various " . "projects.\n"; while ((my $project, my @files) = each(%logs)) { diff --git a/plugins/nginx/nginx_byprojects/byprojects_inout_bandwidth b/plugins/nginx/nginx_byprojects/byprojects_inout_bandwidth old mode 100644 new mode 100755 index 6f62bc53..0feb15fd --- a/plugins/nginx/nginx_byprojects/byprojects_inout_bandwidth +++ b/plugins/nginx/nginx_byprojects/byprojects_inout_bandwidth @@ -1,5 +1,6 @@ #!/usr/bin/perl -w use strict; +use JSON qw(decode_json); # # byprojects_inout_bandwidth # @@ -29,26 +30,23 @@ use strict; # Test graph will be using everything file matching /home/test/log/access*.log # and stuff that match the expression '"[A-Z] /test/' in /var/log/access.log # such as '"GET /test/' +# +# Configuration +# [byprojects_*] +# env.logtail_path /usr/local/bin/logtail +# env.site.prod [{"path":"/home/prod/log/access.log"}] +# env.site.dev [{"path":"/var/log/httpd/ssl-dev-access.log"}, {"path":"/home/dev/log/access*.log"}] +# env.site.test [{"path":"/var/log/access.log","regex":"\"[A-Z]+ /test/"}, {"path":"/home/test/log/access.log"}] my $server = 'Nginx'; -my $statepath = '/usr/local/var/munin/plugin-state'; -my $logtail = '/usr/local/bin/logtail'; - -my %logs = ( - 'prod' => [ - {'path' => '/home/prod/log/access.log'} - ], - 'dev' => [ - {'path' => '/var/log/httpd/ssl-dev-access.log'}, - {'path' => '/home/dev/log/access.log'} - ], - 'test' => [ - {'path' => '/var/log/access.log', 'regex' => '"[A-Z]+ /test/'}, - {'path' => '/home/test/log/access.log'} - ], -); +my $statepath = $ENV{MUNIN_PLUGSTATE}; +my $logtail = $ENV{logtail_path} || '/usr/local/bin/logtail'; +my @loglist = grep {$_ =~ /site\./} keys(%ENV); +my %envLogs = $ENV{@loglist}; +my %logs; +while(my($k, $v) = each %envLogs) { @logs{substr($k, 5)} = decode_json($v); } ########### @@ -60,7 +58,7 @@ if(defined($ARGV[0])) { print "graph_title $server in/out bandwidth byprojects\n"; print "graph_args --base 1000\n"; print "graph_vlabel bits per \${graph_period} in (-) / out (+)\n"; - print "graph_category $server\n"; + print "graph_category webserver\n"; print "graph_info This graph show $server in/out bandwidth used by various" . " projects.\n"; while ((my $project, my @files) = each(%logs)) { diff --git a/plugins/nginx/nginx_connection_request b/plugins/nginx/nginx_connection_request index 47c0faf5..989afa64 100755 --- a/plugins/nginx/nginx_connection_request +++ b/plugins/nginx/nginx_connection_request @@ -132,7 +132,7 @@ if (exists $ARGV[0] and $ARGV[0] eq "autoconf" ) { ## Munin config method. if (exists $ARGV[0] and $ARGV[0] eq "config") { print "graph_title nginx requests/connection handled\n"; - print "graph_category nginx\n"; + print "graph_category webserver\n"; print "graph_vlabel Request/Connection\n"; print "connection_request.label requests/connection\n"; print "connection_request.min 0\n"; diff --git a/plugins/nginx/nginx_error b/plugins/nginx/nginx_error index 7a12262e..6372867a 100755 --- a/plugins/nginx/nginx_error +++ b/plugins/nginx/nginx_error @@ -7,21 +7,27 @@ nginx error - Munin plugin to monitor nginx error rates (http status codes per minute). + =head1 APPLICABLE SYSTEMS -Any Linux host, running nginx, with bash version > 4.0 +Any host running nginx, with bash version > 4.0 + =head1 CONFIGURATION This shows the default configuration of this plugin. You can override the log file path and the logpattern. +Additionally you may want to adjust 'group' (or 'user') based on the +permissions required for reading the log file. [nginx_error] + group adm env.logpath /var/log/nginx env.logpattern a.*.log Nginx must also be configured to log accesses in "combined" log format (default) + =head1 USAGE Link this plugin to /etc/munin/plugins/ and restart the munin-node. @@ -33,51 +39,68 @@ will parse the log file /var/log/nginx/a.mydomaincom.log You can change 'env.logpattern' using asterisk ('*') to match your logs filenames. +'env.logpattern' is ignored for a non-symlink configuration. + + =head1 INTERPRETATION The plugin shows nginx http "error" status rates by parsing access log. + =head1 MAGIC MARKERS #%# family=auto #%# capabilities=autoconf + =head1 BUGS None known. + =head1 VERSION -$Id:$ +1.1 - 2018/01/20 + * add 'dirty config' capability support + * fix shell style issues reported by shellcheck + * improve readability of symlink configuration code + +1.0 - 2017/02/21 + =head1 AUTHOR vovansystems@gmail.com, 2013 + =head1 LICENSE GPLv3 =cut -if [ -z $logpath ]; then - logpath='/var/log/nginx' -fi -name=`basename $0` +set -eu -domain=${name/nginx_error/} -if [[ $domain != '_' && ${#domain} -ne 0 ]]; then - domain=${domain:1} - if [ -z $logpattern ]; then - logpattern='a.*.log' - fi - logpattern=${logpattern/\*/$domain} + +# default environment variable values +logpath=${logpath:-/var/log/nginx} + + +# derive the name of the log file from a potential symlink-configured virtual host +script_name=$(basename "$0") +plugin_suffix=${script_name#nginx_error} +if [ -n "${plugin_suffix#_}" ]; then + # a domain was given via symlink configuration: adjust the logpattern + domain=${plugin_suffix#_} + # default logpattern for symlink configuration mode + logpattern=${logpattern:-a.*.log} + log_filename=${logpattern/\*/$domain} else - logpattern='access.log' + log_filename='access.log' fi -log="$logpath/$logpattern" +log="$logpath/$log_filename" # declaring an array with http status codes, we are interested in declare -A http_codes @@ -94,42 +117,53 @@ http_codes[500]='Internal Server Error' http_codes[502]='Bad Gateway' http_codes[503]='Service Unavailable' -do_ () { # Fetch - declare -A line_counts - values=`awk '{print $9}' $log | sort | uniq -c` - while read -r line; do - read -a tmp <<< "$line"; - line_counts[${tmp[1]}]=${tmp[0]}; - done <<< "$values" - for k in ${!http_codes[@]}; do - echo "error$k.value ${line_counts[$k]:-0}" +# parse error counts from log file +do_fetch () { + local count status_code + declare -A line_counts + while read -r count status_code; do + line_counts[$status_code]=$count + done <<< "$(awk '{print $9}' "$log" | sort | uniq -c)" + + for status_code in "${!http_codes[@]}"; do + echo "error${status_code}.value ${line_counts[$status_code]:-0}" done - exit 0 } + do_config () { - echo "graph_title $logpattern - Nginx errors per minute" - echo 'graph_vlabel pages with http error codes / ${graph_period}' - echo "graph_category nginx" + local status_code + echo "graph_title $(basename "$log") - Nginx errors per minute" + echo "graph_vlabel pages with http error codes / \${graph_period}" + echo "graph_category webserver" echo "graph_period minute" - echo "graph_info This graph shows nginx error amount per minute" - for k in ${!http_codes[@]}; do - echo "error$k.type DERIVE" - echo "error$k.min 0" - echo "error$k.label $k ${http_codes[$k]}" + echo "graph_info This graph shows nginx error rate per minute" + for status_code in "${!http_codes[@]}"; do + echo "error${status_code}.type DERIVE" + echo "error${status_code}.min 0" + echo "error${status_code}.label $status_code ${http_codes[$status_code]}" done - exit 0 } + do_autoconf () { echo yes - exit 0 } -case $1 in - config|autoconf|'') - eval do_$1 + +case ${1:-} in + config) + do_config + # support "dirty config" capability + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = "1" ]; then do_fetch; fi + ;; + autoconf) + do_autoconf + ;; + '') + do_fetch + ;; esac exit $? diff --git a/plugins/nginx/nginx_memory b/plugins/nginx/nginx_memory index 207af3bf..783ef7da 100755 --- a/plugins/nginx/nginx_memory +++ b/plugins/nginx/nginx_memory @@ -75,7 +75,7 @@ MIT if (exists $ARGV[0] and $ARGV[0] eq "config") { print "graph_title nginx RAM usage\n"; print "graph_vlabel RAM\n"; - print "graph_category nginx\n"; + print "graph_category webserver\n"; print "ram.label RAM\n"; print "graph_args --base 1024\n"; diff --git a/plugins/nginx/nginx_upstream b/plugins/nginx/nginx_upstream index ee3ab974..5528e9a1 100755 --- a/plugins/nginx/nginx_upstream +++ b/plugins/nginx/nginx_upstream @@ -18,7 +18,7 @@ if len(sys.argv) > 1: if sys.argv[1]=="config": print "graph_args --base 1000 -l 0" print "graph_title NGINX Upstream times" - print "graph_category nginx" + print "graph_category webserver" print "graph_vlabel milliseconds" print "upstream.label upstream" print "upstream.warning 5000" diff --git a/plugins/nginx/nginx_upstream_multi_ b/plugins/nginx/nginx_upstream_multi_ index d22fc80a..c76f85a5 100755 --- a/plugins/nginx/nginx_upstream_multi_ +++ b/plugins/nginx/nginx_upstream_multi_ @@ -133,7 +133,7 @@ if len(sys.argv) == 2 and sys.argv[1] == "config": print "multigraph nginx_upstream_multi_%s" % siteName.replace(".", "_") print "graph_title Requests number" print "graph_vlabel rps" - print "graph_category nginx" + print "graph_category webserver" for upstream in upstreams.keys(): print "us%s_requests.label %s" % (sanitize(upstream), upstream) @@ -144,7 +144,7 @@ if len(sys.argv) == 2 and sys.argv[1] == "config": print "multigraph nginx_upstream_multi_%s.%s_requests" % (sanitize(siteName), sanitize(upstream)) print "graph_title Requests number - %s" % upstream print "graph_vlabel rps" - print "graph_category nginx" + print "graph_category webserver" print "us%s_requests.label %s" % (sanitize(upstream), upstream) print "" @@ -155,7 +155,7 @@ if len(sys.argv) == 2 and sys.argv[1] == "config": print "multigraph nginx_upstream_multi_%s.%s_times" % (sanitize(siteName), sanitize(upstream)) print "graph_title Request time - %s" % upstream print "graph_vlabel sec." - print "graph_category nginx" + print "graph_category webserver" print "us%s_times.label average" % (sanitize(upstream)) for percentile in percentiles: print "us%s_times_percentile_%s.label %s-percentile" % (sanitize(upstream), percentile, percentile) @@ -168,7 +168,7 @@ if len(sys.argv) == 2 and sys.argv[1] == "config": print "multigraph nginx_upstream_multi_%s.%s_statuses" % (sanitize(siteName), sanitize(upstream)) print "graph_title HTTP - %s" % upstream print "graph_vlabel rps" - print "graph_category nginx" + print "graph_category webserver" keylist = httpStatusList.keys() keylist.sort() for status in keylist: @@ -182,7 +182,7 @@ if len(sys.argv) == 2 and sys.argv[1] == "config": print "multigraph nginx_upstream_multi_%s.%s_cache" % (sanitize(siteName), sanitize(upstream)) print "graph_title Cache - %s" % upstream print "graph_vlabel rps" - print "graph_category nginx" + print "graph_category webserver" for status in cacheStatusList: print "us%s_%s_cache.label %s" % (sanitize(status), sanitize(upstream), status) print "" diff --git a/plugins/nginx/nginx_vhost_traffic b/plugins/nginx/nginx_vhost_traffic index 826059ea..a467d575 100755 --- a/plugins/nginx/nginx_vhost_traffic +++ b/plugins/nginx/nginx_vhost_traffic @@ -33,7 +33,7 @@ LOGDIR=${logdir:-/var/log/nginx} ACCESS_LOG=$LOGDIR/${logfile:-access.log} LOGTAIL=${logtail:-`which logtail`} -STATEFILE=/var/lib/munin/plugin-state/nginx_vhost_traffic.state +STATEFILE=$MUNIN_PLUGSTATE/nginx_vhost_traffic.state VHOSTS=${vhosts:-`hostname`} AGGREGATE=${aggregate:true} @@ -45,7 +45,7 @@ case $1 in echo 'graph_title Nginx Virtual host traffic' echo 'graph_vlabel bits out / ${graph_period}' echo 'graph_args --base 1000 -l 0' - echo 'graph_category Nginx' + echo 'graph_category webserver' i=0 for vhost in $VHOSTS diff --git a/plugins/nginx/nginx_working_set b/plugins/nginx/nginx_working_set index 04e8b809..5de22d8f 100755 --- a/plugins/nginx/nginx_working_set +++ b/plugins/nginx/nginx_working_set @@ -12,7 +12,7 @@ config) cat <<'EOF' graph_title NGINX Working Set graph_vlabel WS Bytes -graph_category nginx +graph_category webserver graph_args --base 1024 ws.label Working Set EOF diff --git a/plugins/nova/nova_floating_ips b/plugins/nova/nova_floating_ips index 8cf6496d..6b7e95ba 100755 --- a/plugins/nova/nova_floating_ips +++ b/plugins/nova/nova_floating_ips @@ -35,7 +35,7 @@ def print_config(): print 'graph_title Nova Floating IPs' print 'graph_vlabel IPs' print 'graph_args --base 1000 --lower-limit 0' - print 'graph_category nova' + print 'graph_category cloud' print 'graph_scale no' print 'graph_info This graph shows the number of Floating IPs in Nova and their status' for state in states: diff --git a/plugins/nova/nova_instance_ b/plugins/nova/nova_instance_ index 32f7de5c..d34730b7 100755 --- a/plugins/nova/nova_instance_ +++ b/plugins/nova/nova_instance_ @@ -75,7 +75,7 @@ def print_config(metric): print 'graph_title Nova Instance %s' % metric print 'graph_vlabel instances' print 'graph_args --base 1000 --lower-limit 0' - print 'graph_category nova' + print 'graph_category cloud' print 'graph_scale no' print 'graph_info This graph shows the number of instances by %s' % metric for state in states: diff --git a/plugins/nova/nova_instance_launched b/plugins/nova/nova_instance_launched index cdd82424..2e80c2ce 100755 --- a/plugins/nova/nova_instance_launched +++ b/plugins/nova/nova_instance_launched @@ -32,7 +32,7 @@ def print_config(): print 'graph_title Nova Instances Launched' print 'graph_vlabel qty' print 'graph_args --base 1000 --lower-limit 0' - print 'graph_category nova' + print 'graph_category cloud' print 'graph_scale no' print 'graph_info This graph shows the number of instances launched since the beginning of time' print 'instances.label instances' diff --git a/plugins/nova/nova_instance_timing b/plugins/nova/nova_instance_timing index 243775af..8e59ca0e 100755 --- a/plugins/nova/nova_instance_timing +++ b/plugins/nova/nova_instance_timing @@ -33,7 +33,7 @@ def print_config(): print 'graph_title Nova Launch Times' print 'graph_vlabel seconds' print 'graph_args --base 1000 --lower-limit 0' - print 'graph_category nova' + print 'graph_category cloud' print 'graph_scale no' print 'graph_info This the average time for the last 5 schedulings/launchings' print 'schedule.label schedule time' diff --git a/plugins/nova/nova_services b/plugins/nova/nova_services index 0daf67ee..d12823ea 100755 --- a/plugins/nova/nova_services +++ b/plugins/nova/nova_services @@ -30,7 +30,7 @@ def print_config(): print 'graph_title Nova Services' print 'graph_vlabel qty' print 'graph_args --base 1000 --lower-limit 0' - print 'graph_category nova' + print 'graph_category cloud' print 'graph_scale no' print 'graph_info Nova services - alive and active' for service in services: diff --git a/plugins/time/ntp_kernel_pll_prec b/plugins/ntp/ntp_kernel_pll_prec similarity index 100% rename from plugins/time/ntp_kernel_pll_prec rename to plugins/ntp/ntp_kernel_pll_prec diff --git a/plugins/time/ntp_kernel_pll_tol b/plugins/ntp/ntp_kernel_pll_tol similarity index 100% rename from plugins/time/ntp_kernel_pll_tol rename to plugins/ntp/ntp_kernel_pll_tol diff --git a/plugins/time/ntp_packets b/plugins/ntp/ntp_packets similarity index 58% rename from plugins/time/ntp_packets rename to plugins/ntp/ntp_packets index 9b7d653d..1fb19b05 100755 --- a/plugins/time/ntp_packets +++ b/plugins/ntp/ntp_packets @@ -13,7 +13,7 @@ # Symlink this plugin into the node's plugins directory (like # /etc/munin/plugins). # -# Copyright © 2013 Kenyon Ralph +# Copyright © 2016 Kenyon Ralph # # This program is free software. It comes without any warranty, to the # extent permitted by applicable law. You can redistribute it and/or @@ -34,7 +34,8 @@ import sys if len(sys.argv) == 2 and sys.argv[1] == 'config': print('graph_title NTP traffic') print('graph_vlabel Packets/${graph_period} received(-)/sent(+)') - print('graph_info This graph shows the packet rates of this ntpd. Ignored and dropped packets are graphed as positive values.') + print('graph_info This graph shows the packet rates of this ntpd. Bad means packets received ' + 'with bad length or format. Authfailed means packets for which authentication failed.') print('graph_category time') print('received.label Received') print('received.type DERIVE') @@ -50,6 +51,21 @@ if len(sys.argv) == 2 and sys.argv[1] == 'config': print('ignored.label Ignored') print('ignored.type DERIVE') print('ignored.min 0') + print('bad.label Bad') + print('bad.type DERIVE') + print('bad.min 0') + print('authfail.label Authfailed') + print('authfail.type DERIVE') + print('authfail.min 0') + print('declined.label Declined') + print('declined.type DERIVE') + print('declined.min 0') + print('restricted.label Restricted') + print('restricted.type DERIVE') + print('restricted.min 0') + print('kod.label KoD responses') + print('kod.type DERIVE') + print('kod.min 0') sys.exit(0) os.environ['PATH'] = '/usr/local/sbin:/usr/local/bin:' + os.environ['PATH'] @@ -57,22 +73,30 @@ os.environ['PATH'] = '/usr/local/sbin:/usr/local/bin:' + os.environ['PATH'] # Assuming that the ntpd version is the same as the ntpq or ntpdc # version. This is how a proper install should be. -version = subprocess.check_output(['ntpq', '-c', 'version'], universal_newlines=True).split()[1][0:5].replace('.', '') +version = subprocess.check_output(['ntpq', '-c', 'version'], + universal_newlines=True).split()[1][0:5].replace('.', '') if int(version) >= 427: cmd = 'ntpq' else: cmd = 'ntpdc' -iostats = dict() +stats = dict() -iostats_output = subprocess.check_output([cmd, '-c', 'iostats'], universal_newlines=True).splitlines() +stats_output = subprocess.check_output([cmd, '-c', 'iostats', '-c', 'sysstats'], + universal_newlines=True).splitlines() -for line in iostats_output: iostats[line.split(':')[0]] = int(line.split(':')[1]) +for line in stats_output: + stats[line.split(':')[0]] = int(line.split(':')[1]) -print('received.value ' + str(iostats['received packets'])) -print('sent.value ' + str(iostats['packets sent'])) -print('dropped.value ' + str(iostats['dropped packets'])) -print('ignored.value ' + str(iostats['ignored packets'])) +print('received.value ' + str(stats['received packets'])) +print('sent.value ' + str(stats['packets sent'])) +print('dropped.value ' + str(stats['dropped packets'])) +print('ignored.value ' + str(stats['ignored packets'])) +print('bad.value ' + str(stats['bad length or format'])) +print('authfail.value ' + str(stats['authentication failed'])) +print('declined.value ' + str(stats['declined'])) +print('restricted.value ' + str(stats['restricted'])) +print('kod.value ' + str(stats['KoD responses'])) sys.exit(0) diff --git a/plugins/time/ntp_peers b/plugins/ntp/ntp_peers similarity index 98% rename from plugins/time/ntp_peers rename to plugins/ntp/ntp_peers index 0350cf75..174a0c26 100755 --- a/plugins/time/ntp_peers +++ b/plugins/ntp/ntp_peers @@ -60,7 +60,7 @@ use Socket; my $NTPQ = $ENV{ntpq} || "ntpq"; my $COMMAND = "$NTPQ -np"; -my $statedir = $ENV{statedir} || '/var/lib/munin/plugin-state'; +my $statedir = $ENV{statedir} || $ENV{MUNIN_PLUGSTATE}; my $statefile = "$statedir/ntp_peers.state"; # autoconf diff --git a/plugins/time/ntp_peers_ipv6 b/plugins/ntp/ntp_peers_ipv6 similarity index 98% rename from plugins/time/ntp_peers_ipv6 rename to plugins/ntp/ntp_peers_ipv6 index 803a8fc6..d98701e0 100755 --- a/plugins/time/ntp_peers_ipv6 +++ b/plugins/ntp/ntp_peers_ipv6 @@ -46,7 +46,7 @@ use strict; my $NTPQ = $ENV{ntpq} || "ntpq"; my $COMMAND = "$NTPQ -nc associations"; -my $statedir = $ENV{statedir} || '/usr/local/var/munin/plugin-state'; +my $statedir = $ENV{statedir} || $ENV{MUNIN_PLUGSTATE}; my $statefile = "$statedir/ntp_peers.state"; # autoconf diff --git a/plugins/time/ntp_pool_score_ b/plugins/ntp/ntp_pool_score_ similarity index 100% rename from plugins/time/ntp_pool_score_ rename to plugins/ntp/ntp_pool_score_ diff --git a/plugins/time/ntp_queries b/plugins/ntp/ntp_queries similarity index 100% rename from plugins/time/ntp_queries rename to plugins/ntp/ntp_queries diff --git a/plugins/time/ntpdate_ b/plugins/ntp/ntpdate_ similarity index 100% rename from plugins/time/ntpdate_ rename to plugins/ntp/ntpdate_ diff --git a/plugins/sensors/omreport_fan_speed b/plugins/omreport/omreport_fan_speed similarity index 100% rename from plugins/sensors/omreport_fan_speed rename to plugins/omreport/omreport_fan_speed diff --git a/plugins/sensors/omreport_pwrmon_current b/plugins/omreport/omreport_pwrmon_current similarity index 100% rename from plugins/sensors/omreport_pwrmon_current rename to plugins/omreport/omreport_pwrmon_current diff --git a/plugins/sensors/omreport_pwrmon_power b/plugins/omreport/omreport_pwrmon_power similarity index 100% rename from plugins/sensors/omreport_pwrmon_power rename to plugins/omreport/omreport_pwrmon_power diff --git a/plugins/sensors/omreport_storage_temp b/plugins/omreport/omreport_storage_temp similarity index 100% rename from plugins/sensors/omreport_storage_temp rename to plugins/omreport/omreport_storage_temp diff --git a/plugins/sensors/omreport_temp b/plugins/omreport/omreport_temp similarity index 100% rename from plugins/sensors/omreport_temp rename to plugins/omreport/omreport_temp diff --git a/plugins/openfire/openfire_ b/plugins/openfire/openfire_ index 8c04ec4a..3e992f9e 100755 --- a/plugins/openfire/openfire_ +++ b/plugins/openfire/openfire_ @@ -73,7 +73,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { print "graph_args --base 1000 -l 0\n"; print "graph_title Openfire DB Usage\n"; print "graph_vlabel Number of Connections\n"; - print "graph_category Openfire\n"; + print "graph_category chat\n"; print "graph_info The Graph shows Number of DB Connections\n"; print "db_used.draw AREA\ndb_used.label used\ndb_used.type GAUGE\n"; print "db_current.draw STACK\ndb_current.label current\ndb_current.type GAUGE\n"; @@ -85,7 +85,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { print "graph_scale no\n"; print "graph_title Openfire Threads\n"; print "graph_vlabel Number of Threads\n"; - print "graph_category Openfire\n"; + print "graph_category chat\n"; print "graph_info The Graph shows Number of Threads\n"; print "thread_core.draw AREA\nthread_core.label Core Threads\nthread_core.type GAUGE\n"; print "thread_active.draw STACK\nthread_active.label active Threads\nthread_active.type GAUGE\n"; @@ -95,7 +95,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { print "graph_scale no\n"; print "graph_title Openfire Tasks\n"; print "graph_vlabel Number of Tasks\n"; - print "graph_category Openfire\n"; + print "graph_category chat\n"; print "graph_info The Graph shows Number of Tasks\n"; print "task_queue.draw AREA\ntask_queue.label queued Tasks\ntask_queue.type GAUGE\n"; print "task_completed.draw STACK\ntask_completed.label completed Tasks\ntask_completed.type DERIVE\n"; @@ -105,7 +105,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { print "graph_scale no\n"; print "graph_title Openfire Sessions\n"; print "graph_vlabel Number of Sessions\n"; - print "graph_category Openfire\n"; + print "graph_category chat\n"; print "graph_info The Graph shows Number of Jabber Sessions\n"; print "sessions.label Jabber Sessions\nsessions.type GAUGE\n"; } @@ -113,7 +113,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { print "graph_args --base 1000 -l 0\n"; print "graph_title Openfire I/O (NIO)\n"; print "graph_vlabel Reads/Writes per \${graph_period} in (-) / out (+)\n"; - print "graph_category Openfire\n"; + print "graph_category chat\n"; print "graph_info The Graph shows Number of Stanzas\n"; print "nio_written.label nio\nnio_written.draw LINE1\nnio_written.max 10000\nnio_written.min 0\nnio_written.graph no\n"; print "nio_read.label nio\nnio_read.draw LINE1\nnio_read.max 10000\nnio_read.min 0\nnio_read.negative nio_written\nnio_read.type DERIVE\n"; @@ -122,7 +122,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { print "graph_args --base 1000 -l 0\n"; print "graph_title Openfire Queue\n"; print "graph_vlabel Number of queued Stanzas\n"; - print "graph_category Openfire\n"; + print "graph_category chat\n"; print "graph_info The Graph shows Number of queued Stanzas\n"; print "nio_queued_events.draw AREA\nnio_queued_events.label queued Events\nnio_queued_events.type GAUGE\n"; print "nio_queued_writes.draw STACK\nnio_queued_writes.label queued Writes\nnio_queued_writes.type GAUGE\n"; @@ -131,7 +131,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { print "graph_args --base 1000 -l 0\n"; print "graph_title Openfire Log Timestamp\n"; print "graph_vlabel Timestamp\n"; - print "graph_category Openfire\n"; + print "graph_category chat\n"; print "graph_info The Graph shows the Timestamp of the Log\n"; print "timestamp.draw AREA\ntimestamp.label Log Timestamp\ntimestamp.type GAUGE\n"; } @@ -173,4 +173,4 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { print "nio_queued_events.value $array[12-$diff]\n"; print "nio_queued_writes.value $array[13-$diff]"; } -} \ No newline at end of file +} diff --git a/plugins/openvz/openvz-load-avg b/plugins/openvz/openvz-load-avg index 42c6deb8..77dbcb42 100755 --- a/plugins/openvz/openvz-load-avg +++ b/plugins/openvz/openvz-load-avg @@ -57,7 +57,7 @@ if ( $arg eq "autoconf" ) { print "graph_scale no\n"; print "graph_vlabel load average\n"; print "graph_info Shows 5-minute load average per VE\n"; - print "graph_category OpenVZ\n"; + print "graph_category virtualization\n"; # Dynamic config my @result=`/usr/sbin/vzlist -H -a -s veid -o veid,name`; diff --git a/plugins/openvz/openvz_ b/plugins/openvz/openvz_ index 4044ade7..cf1484b5 100755 --- a/plugins/openvz/openvz_ +++ b/plugins/openvz/openvz_ @@ -13,13 +13,23 @@ # $Log$ # #%# family=auto -#%# capabilities=autoconf +#%# capabilities=autoconf suggest -ATTRIBUTE=`basename $0 | sed 's/^openvz_//g'` +ATTRIBUTE=$(basename "$0" | sed 's/^openvz_//g') if [ "$1" = "autoconf" ]; then - echo yes + if which vzlist >/dev/null; then + echo yes + else + echo "no (missing 'vzlist' executable)" + fi + exit 0 +fi + +if [ "$1" = "suggest" ]; then + echo "laverage" + echo "status" exit 0 fi @@ -28,11 +38,11 @@ if [ "$1" = "config" ]; then echo "graph_args --base 1000 -l 0" echo "graph_scale yes" echo "graph_vlabel $ATTRIBUTE Value" - echo "graph_category openvz" + echo "graph_category virtualization" echo "graph_info This graph shows OpenVZ: $ATTRIBUTE" - vzlist -a -H -o hostname | awk '{gsub(/\./,"_",$1) - print("'$ATTRIBUTE'"$1".label "$1"\n" \ - "'$ATTRIBUTE'"$1".info '$ATTRIBUTE' for VE"$1)}' + vzlist -a -H --no-trim -o hostname | awk '{gsub(/\./,"_",$1) + print("'$ATTRIBUTE'"$1".label "$1"\n" \ + "'$ATTRIBUTE'"$1".info '$ATTRIBUTE' for VE"$1)}' exit 0 fi @@ -41,8 +51,7 @@ filter() { cat; } [ "$ATTRIBUTE" = 'status' ] && filter() { sed -e 's/running/10/g;s/stopped/0/g'; } #TODO: filter for uptime -vzlist -a -H -o hostname,$ATTRIBUTE | filter | awk '{gsub(/\./,"_",$1) +vzlist -a -H --no-trim -o "hostname,$ATTRIBUTE" | filter | awk '{gsub(/\./,"_",$1) print("'$ATTRIBUTE'"$1".value "$2)}' exit 0 - diff --git a/plugins/openvz/openvzcpu b/plugins/openvz/openvzcpu index 1806de6d..288f81ca 100755 --- a/plugins/openvz/openvzcpu +++ b/plugins/openvz/openvzcpu @@ -3,7 +3,7 @@ # vim: sts=8 sw=8 ts=8 # -# Run "perldoc thisfilename" to get a well-formated set of documentation +# Run "perldoc thisfilename" to get a well-formatted set of documentation # for this munin plugin. # =head1 NAME @@ -224,7 +224,7 @@ graph_title OpenVZ container CPU usage graph_vlabel CPU cores used ($cores available) graph_scale no graph_info This graph shows how CPU time is spent. -graph_category openvz +graph_category virtualization graph_period second STDCONFIG if (0) { diff --git a/plugins/system/vpsmem b/plugins/openvz/vpsmem similarity index 98% rename from plugins/system/vpsmem rename to plugins/openvz/vpsmem index 57014783..c9d9d789 100755 --- a/plugins/system/vpsmem +++ b/plugins/openvz/vpsmem @@ -29,7 +29,7 @@ if [ "$1" == "config" ]; then cut -c9- <config_session(); + + print "host_name $host\n" unless ($host eq 'localhost'); + print <<'EOC'; +graph_title Memory usage +graph_args --base 1000 -l 0 +graph_vlabel kB +graph_category memory +graph_info This graph shows total and used memory on the host. +memsize.label total +memused.label used +memsize.info The total memory. +memused.info The memory in use. +memsize.draw LINE1 +memused.draw LINE2 +EOC + exit 0; +} + +my $session = Munin::Plugin::SNMP->session(); +print "memsize.value ", $session->get_single('1.3.6.1.2.1.25.2.3.1.5.1'), "\n"; +print "memused.value ", $session->get_single('1.3.6.1.2.1.25.2.3.1.6.1'), "\n"; diff --git a/plugins/oracle/example-graphs/oracle_sysstat-1.png b/plugins/oracle/example-graphs/oracle_sysstat-1.png new file mode 100644 index 00000000..423d736d Binary files /dev/null and b/plugins/oracle/example-graphs/oracle_sysstat-1.png differ diff --git a/plugins/oracle/example-graphs/oracle_sysstat-2.png b/plugins/oracle/example-graphs/oracle_sysstat-2.png new file mode 100644 index 00000000..537cd322 Binary files /dev/null and b/plugins/oracle/example-graphs/oracle_sysstat-2.png differ diff --git a/plugins/oracle/oracle-pga-monitor b/plugins/oracle/oracle-pga-monitor index ec8e9a77..a0eaa1a3 100755 --- a/plugins/oracle/oracle-pga-monitor +++ b/plugins/oracle/oracle-pga-monitor @@ -99,7 +99,7 @@ if ARGV[0] == "autoconf" elsif ARGV[0] == "config" puts "graph_args --base 1024k -r --lower-limit 0" puts "graph_title Oracle PGA from #{dbname}" - puts "graph_category Oracle" + puts "graph_category db" puts "graph_info This graph shows the PGA memory usage (in MB)" puts "graph_vlabel MB" puts "graph_scale no" diff --git a/plugins/oracle/oracle-sga b/plugins/oracle/oracle-sga index afadb99f..d167296d 100755 --- a/plugins/oracle/oracle-sga +++ b/plugins/oracle/oracle-sga @@ -124,7 +124,7 @@ if ARGV[0] == "autoconf" elsif ARGV[0] == "config" puts "graph_args --base 1024k -r --lower-limit 0" puts "graph_title Oracle SGA from #{dbname}" - puts "graph_category Oracle" + puts "graph_category db" puts "graph_info This graph shows the SGA memory usage (in MB)" puts "graph_vlabel MB" puts "graph_scale no" diff --git a/plugins/oracle/oracle__connections b/plugins/oracle/oracle__connections index 48e071a9..5119e840 100755 --- a/plugins/oracle/oracle__connections +++ b/plugins/oracle/oracle__connections @@ -87,7 +87,7 @@ if (exists $ARGV[0]) { print "graph_title Oracle active connections from $dbname\n"; print "graph_args -l 0 --base 1000\n"; print "graph_vlabel Connections\n"; - print "graph_category oracle\n"; + print "graph_category db\n"; print "graph_info Shows active oracle connections from $dbname\n"; print "graph_scale no\n"; if ( $showusers ) { diff --git a/plugins/oracle/oracle__database_hitratio b/plugins/oracle/oracle__database_hitratio index 15d696ef..e07dba87 100755 --- a/plugins/oracle/oracle__database_hitratio +++ b/plugins/oracle/oracle__database_hitratio @@ -78,7 +78,7 @@ if (exists $ARGV[0]) { print "graph_title Oracle library and buffer cache hit ratios from $dbname\n"; print "graph_args --upper-limit 110 -l 0\n"; print "graph_vlabel %\n"; - print "graph_category Oracle\n"; + print "graph_category db\n"; print "graph_info This graph shows the percentage of blocks and libraries read from the cache\n"; print "read_hitratio.label Cache hitratio\n"; print "read_hitratio.type GAUGE\n"; diff --git a/plugins/oracle/oracle__locks b/plugins/oracle/oracle__locks index 1aef595f..d8140fbc 100755 --- a/plugins/oracle/oracle__locks +++ b/plugins/oracle/oracle__locks @@ -79,7 +79,7 @@ if (exists $ARGV[0]) { print "graph_title Oracle locks for $dbname\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Locks\n"; - print "graph_category oracle\n"; + print "graph_category db\n"; print "graph_info Shows oracle locks\n"; print "graph_scale no\n"; print "RS.label Row Share Locks\n"; diff --git a/plugins/oracle/oracle__tablespace_usage b/plugins/oracle/oracle__tablespace_usage index e2d6cca5..c35235a0 100755 --- a/plugins/oracle/oracle__tablespace_usage +++ b/plugins/oracle/oracle__tablespace_usage @@ -80,7 +80,7 @@ if (exists $ARGV[0]) { print "graph_title Oracle tablespace usage (in %) from $dbname\n"; print "graph_args --upper-limit 100 -l 0\n"; print "graph_vlabel %\n"; - print "graph_category Oracle\n"; + print "graph_category db\n"; print "graph_info This graph shows the tablespace usage (in %)\n"; print "graph_scale no\n"; print "warning $warning\n"; diff --git a/plugins/oracle/oracle_connections b/plugins/oracle/oracle_connections index c039da4d..a05f5d8c 100755 --- a/plugins/oracle/oracle_connections +++ b/plugins/oracle/oracle_connections @@ -103,7 +103,7 @@ EOF` echo "graph_title Oracle active connections to $ORACLE_SID" echo "graph_args -l 0 --base 1000" echo "graph_vlabel Connections" - echo "graph_category Oracle" + echo "graph_category db" echo "graph_info Shows active oracle connections to $ORACLE_SID" echo "graph_scale no" if [ $SHOW_ORACLE_USERS -eq 1 ] diff --git a/plugins/oracle/oracle_sysmetricspl b/plugins/oracle/oracle_sysmetricspl index 22cafc41..27989c8d 100755 --- a/plugins/oracle/oracle_sysmetricspl +++ b/plugins/oracle/oracle_sysmetricspl @@ -111,7 +111,7 @@ sub printconfig { if ($name eq "transactions") { print "graph_title User transactions\n"; print "graph_vlabel transactions per second\n"; - print "graph_category Oracle\n"; + print "graph_category db\n"; print "graph_info This graph shows the number of users transactions per second in Oracle\n"; print "graph_args --base 1000 -l 0\n"; print "transactions.label transactions\n"; @@ -120,7 +120,7 @@ sub printconfig { } elsif ($name eq "indexscans") { print "graph_title Full index scans\n"; print "graph_vlabel indexscans per second\n"; - print "graph_category Oracle\n"; + print "graph_category db\n"; print "graph_info This graph shows the number of full index scans per second in Oracle\n"; print "graph_args --base 1000 -l 0\n"; print "indexscans.label indexscans\n"; diff --git a/plugins/oracle/oracle_sysstat b/plugins/oracle/oracle_sysstat new file mode 100755 index 00000000..85ddd1f9 --- /dev/null +++ b/plugins/oracle/oracle_sysstat @@ -0,0 +1,1129 @@ +#!/bin/bash +# -*- sh -*- + +: << =cut + +=head1 NAME + + oracle_sysstat - Munin multi-graph plugin to monitor Oracle Statistics + + These modules are implemented: + execute - To monitor Oracle Sysstat SQL Execute Count + parse - To monitor Oracle Sysstat SQL Parse Count + tablefetch - To monitor Oracle Sysstat SQL Table Fetch Rows + tablescan - To monitor Oracle Sysstat SQL Table Scans + transaction - To monitor Oracle Sysstat SQL Transactions + sort - To monitor Oracle Sysstat SQL Sorts + logon - To monitor Oracle Sysstat User Logons + cursor - To monitor Oracle Sysstat User Opened Cursors + enqueue - To monitor Oracle Sysstat Enqueues + redolog - To monitor Oracle Sysstat Redo Entries + redosize - To monitor Oracle Sysstat Redo Size + physicaliops - To monitor Oracle Sysstat I/O Physical Requests + physicalrw - To monitor Oracle Sysstat I/O Physical Bytes + blockrw - To monitor Oracle Sysstat I/O Blocks + netrw - To monitor Oracle Sysstat I/O Network Bytes + sgainfo - To monitor Oracle Memory SGA + pgastat - To monitor Oracle Memory PGA + cputime - To monitor Oracle CPU Time + cachehit - To monitor Oracle Cache Hit Ratio + sessionuser - To monitor Oracle Session Users + sessionwait - To monitor Oracle Session Wait + eventwait - To monitor Oracle Wait Events + eventwaitapplication - To monitor Oracle Wait Events Application + eventwaitnetwork - To monitor Oracle Wait Events Network + eventwaitconcurrency - To monitor Oracle Wait Events Concurrency + eventwaituserio - To monitor Oracle Wait Events User I/O + eventwaitsystemio - To monitor Oracle Wait Events System I/O + eventwaitcluster - To monitor Oracle Wait Events Cluster + eventwaitadministrative - To monitor Oracle Wait Events Administrative + eventwaitconfiguration - To monitor Oracle Wait Events Configuration + tablespace - To monitor Oracle Table Space Usage + asmusage - To monitor Oracle ASM Disk Group Usage + +=head1 CONFIGURATION + + Make symlink: + cd /path/to/munin/etc/plugins + ln -s /path/to/munin/lib/plugins/oracle_sysstat . + ln -s /path/to/munin/lib/plugins/oracle_sysstat oracle_sysstat_asmusage # if necessary + ... + + The following shows example settings for this plugin: + + [oracle_sysstat] + user oracle + env.ORACLE_SID ORCL + env.ORACLE_HOME /path/to/oracle/home + env.oracle_auth / as SYSDBA + + [oracle_sysstat_asmusage] + user grid + env.ORACLE_SID +ASM + env.ORACLE_HOME /path/to/grid/home + env.oracle_auth / as SYSASM + env.include_module asmusage + env.plugin_name oracle_sysstat + +=head1 ENVIRONMENT VARIABLES + + env.ORACLE_SID: + example: env.ORACLE_SID SOMESID + default: ORCL + + env.ORACLE_HOME: + example: env.ORACLE_HOME /opt/oracle/... + default: Try to find from oratab file + + env.oracle_auth: + example: env.oracle_auth user/pass as SYSDBA + default: / as SYSDBA + + env.exclude_module: + example: env.exclude_module asmusage tablespace + default: asmusage + + Module name(s) to exclude seperated by white-space. + By default, asmusage module is excluded because another privilege + is necessary to connect ASM instance. + + env.include_module: + example: env.include_module asmusage + default: none + + Module name(s) to include seperated by white-space. + If both include_module and exclude_module are set, exclude will be + ignored. + + env.plugin_name: + example: env.plugin_name oracle_sysstat_2 + default: program name (usually oracle_sysstat) + + Used for internal graph name. + It will be useful to monitor multi-instance databases. + + env.db_name: + example: env.db_name dbname + default: none + + Used for graph title. + It will be useful to monitor multi-instance databases. + +=head1 NOTES + + Uses the command "sqlplus". + Tested with Oracle Database 12c R1. + +=head1 AUTHOR + + K.Cima https://github.com/shakemid + +=head1 LICENSE + + GPLv2 + +=head1 MAGIC MARKERS + + #%# family=contrib + #%# capabilities=autoconf + +=cut + +# Include plugin.sh +. "${MUNIN_LIBDIR:-}/plugins/plugin.sh" +is_multigraph "$@" + +# Like perl 'use strict;' +set -o nounset + +# Global variables +: "${ORACLE_SID:=ORCL}" +: "${ORACLE_HOME:=$( cat /etc/oratab /var/opt/oracle/oratab \ + | awk -F: '$1 == "'$ORACLE_SID'" { print $2 }' 2>/dev/null )}" +: "${oracle_auth:=/ as SYSDBA}" +: "${exclude_module:=asmusage}" +: "${include_module:=}" +: "${plugin_name:=${0##*/}}" +[ -n "${db_name:=}" ] && db_name=" ($db_name)" + +PATH=$PATH:$ORACLE_HOME/bin +export PATH ORACLE_HOME ORACLE_SID + +# Graph settings +declare -A global_attrs # required +declare -A data_attrs # required (format: field type draw label) +declare -A getfield_func # optional +declare -A getvalue_func # required + +# Note: Bash 4 (or above) is required to use hash. + +key=execute +global_attrs[$key]=" + graph_title Oracle$db_name Sysstat SQL Execute Count + graph_category db + graph_args --base 1000 --lower-limit 0 --rigid + graph_vlabel count per second + graph_info Oracle Sysstat SQL Execute Count +" +data_attrs[$key]=" + execute_count DERIVE LINE execute count + user_calls DERIVE LINE user calls + recursive_calls DERIVE LINE recursive calls +" +getvalue_func[$key]=getvalue_sysstat + +key=parse +global_attrs[$key]=" + graph_title Oracle$db_name Sysstat SQL Parse Count + graph_category db + graph_args --base 1000 --lower-limit 0 --rigid + graph_vlabel count per second + graph_info Oracle Sysstat SQL Parse Count +" +data_attrs[$key]=" + parse_count_total DERIVE LINE parse count (total) + parse_count_hard DERIVE LINE parse count (hard) + parse_count_describe DERIVE LINE parse count (describe) + parse_count_failures DERIVE LINE parse count (failures) +" +getvalue_func[$key]=getvalue_sysstat + +key=tablefetch +global_attrs[$key]=" + graph_title Oracle$db_name Sysstat SQL Table Fetch Rows + graph_category db + graph_args --base 1000 --lower-limit 0 --rigid + graph_vlabel count per second + graph_info Oracle Sysstat SQL Table Fetch Rows +" +data_attrs[$key]=" + table_fetch_by_rowid DERIVE LINE table fetch by rowid + table_scan_rows_gotten DERIVE LINE table scan rows gotten + table_fetch_continued_row DERIVE LINE table fetch continued row +" +getvalue_func[$key]=getvalue_sysstat + +key=tablescan +global_attrs[$key]=" + graph_title Oracle$db_name Sysstat SQL Table Scans + graph_category db + graph_args --base 1000 --lower-limit 0 --rigid + graph_vlabel count per second + graph_info Oracle Sysstat SQL Table Scans +" +data_attrs[$key]=" + table_scans_short_tables DERIVE LINE table scans (short tables) + table_scans_long_tables DERIVE LINE table scans (long tables) +" +getvalue_func[$key]=getvalue_sysstat + +key=transaction +global_attrs[$key]=" + graph_title Oracle$db_name Sysstat SQL Transactions + graph_category db + graph_args --base 1000 --lower-limit 0 --rigid + graph_vlabel count per second + graph_info Oracle Sysstat SQL Transactions +" +data_attrs[$key]=" + user_commits DERIVE LINE user commits + user_rollbacks DERIVE LINE user rollbacks +" +getvalue_func[$key]=getvalue_sysstat + +key=sort +global_attrs[$key]=" + graph_title Oracle$db_name Sysstat SQL Sorts + graph_category db + graph_args --base 1000 --lower-limit 0 --rigid + graph_vlabel count per second + graph_info Oracle Sysstat SQL Sorts +" +data_attrs[$key]=" + sorts_memory DERIVE LINE sorts (memory) + sorts_disk DERIVE LINE sorts (disk) +" +getvalue_func[$key]=getvalue_sysstat + +key=logon +global_attrs[$key]=" + graph_title Oracle$db_name Sysstat User Logons + graph_category db + graph_args --base 1000 --lower-limit 0 --rigid + graph_vlabel count per second + graph_info Oracle Sysstat User Logons +" +data_attrs[$key]=" + logon DERIVE LINE logons cumulative +" +getvalue_func[$key]=getvalue_sysstat + +key=cursor +global_attrs[$key]=" + graph_title Oracle$db_name Sysstat User Opened Cursors + graph_category db + graph_args --base 1000 --lower-limit 0 --rigid + graph_vlabel count + graph_info Oracle Sysstat User Opened Cursors +" +data_attrs[$key]=" + open_cursor GAUGE LINE opened cursors current +" +getvalue_func[$key]=getvalue_sysstat + +key=enqueue +global_attrs[$key]=" + graph_title Oracle$db_name Sysstat Enqueues + graph_category db + graph_args --base 1000 --lower-limit 0 --rigid + graph_vlabel count per second + graph_info Oracle Sysstat Enqueues +" +data_attrs[$key]=" + enqueue_requests DERIVE LINE enqueue requests + enqueue_releases DERIVE LINE enqueue releases + enqueue_conversions DERIVE LINE enqueue conversions + enqueue_waits DERIVE LINE enqueue waits + enqueue_timeouts DERIVE LINE enqueue timeouts + enqueue_deadlocks DERIVE LINE enqueue deadlocks +" +getvalue_func[$key]=getvalue_sysstat + +key=redolog +global_attrs[$key]=" + graph_title Oracle$db_name Sysstat Redo Entries + graph_category db + graph_args --base 1000 --lower-limit 0 --rigid + graph_vlabel count per second + graph_info Oracle Sysstat Redo Entries +" +data_attrs[$key]=" + redo_entries DERIVE LINE redo entries + redo_writes DERIVE LINE redo writes + redo_synch_writes DERIVE LINE redo synch writes + redo_buffer_allocation_retries DERIVE LINE redo buffer allocation retries + redo_log_space_requests DERIVE LINE redo log space requests +" +getvalue_func[$key]=getvalue_sysstat + +key=redosize +global_attrs[$key]=" + graph_title Oracle$db_name Sysstat Redo Size + graph_category db + graph_args --base 1024 --lower-limit 0 --rigid + graph_vlabel bytes per second + graph_info Oracle Sysstat Redo Size +" +data_attrs[$key]=" + redo_size DERIVE LINE redo size + redo_wastage DERIVE LINE redo wastage +" +getvalue_func[$key]=getvalue_sysstat + +key=physicaliops +global_attrs[$key]=" + graph_title Oracle$db_name Sysstat I/O Physical Requests + graph_category db + graph_args --base 1000 --lower-limit 0 --rigid + graph_vlabel iops + graph_info Oracle Sysstat I/O Physical Requests +" +data_attrs[$key]=" + physical_read_total DERIVE LINE2 physical read total IO requests + physical_read DERIVE LINE physical read IO requests + physical_read_total_multi DERIVE LINE physical read total multi block requests + physical_write_total DERIVE LINE2 physical write total IO requests + physical_write DERIVE LINE physical write IO requests + physical_write_total_multi DERIVE LINE physical write total multi block requests +" +getvalue_func[$key]=getvalue_sysstat + +key=physicalrw +global_attrs[$key]=" + graph_title Oracle$db_name Sysstat I/O Physical Bytes + graph_category db + graph_args --base 1024 --lower-limit 0 --rigid + graph_vlabel bytes per second + graph_info Oracle Sysstat I/O Physical Bytes +" +data_attrs[$key]=" + physical_read_total DERIVE LINE2 physical read total bytes + physical_read DERIVE LINE physical read bytes + physical_write_total DERIVE LINE2 physical write total bytes + physical_write DERIVE LINE physical write bytes +" +getvalue_func[$key]=getvalue_sysstat + +key=blockrw +global_attrs[$key]=" + graph_title Oracle$db_name Sysstat I/O Blocks + graph_category db + graph_args --base 1000 --lower-limit 0 --rigid + graph_vlabel blocks per second + graph_info Oracle Sysstat I/O Blocks +" +data_attrs[$key]=" + db_block_gets DERIVE LINE db block gets + db_block_changes DERIVE LINE db block changes + consistent_gets DERIVE LINE consistent gets + consistent_changes DERIVE LINE consistent changes + physical_reads DERIVE LINE physical reads + physical_writes DERIVE LINE physical writes +" +getvalue_func[$key]=getvalue_sysstat + + +key=netrw +global_attrs[$key]=" + graph_title Oracle$db_name Sysstat I/O Network Bytes + graph_category db + graph_args --base 1024 --lower-limit 0 --rigid + graph_vlabel bytes per second + graph_info Oracle Sysstat I/O Network Bytes +" +data_attrs[$key]=" + bytes_sent_via_sql_net_to_client DERIVE LINE bytes sent via SQL*Net to client + bytes_received_via_sql_net_from_client DERIVE LINE bytes received via SQL*Net from client + bytes_sent_via_sql_net_to_dblink DERIVE LINE bytes sent via SQL*Net to dblink + bytes_received_via_sql_net_from_dblink DERIVE LINE bytes received via SQL*Net from dblink +" +getvalue_func[$key]=getvalue_sysstat + +key=sgainfo +global_attrs[$key]=" + graph_title Oracle$db_name Memory SGA + graph_category db + graph_args --base 1024 --lower-limit 0 --rigid + graph_vlabel bytes + graph_info Oracle Memory SGA +" +data_attrs[$key]=" + fixed_sga_size GAUGE AREASTACK Fixed SGA Size + redo_buffers GAUGE AREASTACK Redo Buffers + shared_pool_size GAUGE AREASTACK Shared Pool Size + large_pool_size GAUGE AREASTACK Large Pool Size + java_pool_size GAUGE AREASTACK Java Pool Size + streams_pool_size GAUGE AREASTACK Streams Pool Size + shared_io_pool_size GAUGE AREASTACK Shared IO Pool Size + buffer_cache_size GAUGE AREASTACK Buffer Cache Size + in_memory_area_size GAUGE AREASTACK In-Memory Area Size + maximum_sga_size GAUGE LINE Maximum SGA Size +" +getvalue_func[$key]=getvalue_sgainfo + +key=pgastat +global_attrs[$key]=" + graph_title Oracle$db_name Memory PGA + graph_category db + graph_args --base 1024 --lower-limit 0 --rigid + graph_vlabel bytes + graph_info Oracle Memory PGA +" +data_attrs[$key]=" + pga_inuse GAUGE AREA total PGA inuse + pga_allocated GAUGE LINE total PGA allocated + pga_target GAUGE LINE aggregate PGA target parameter + pga_auto_target GAUGE LINE aggregate PGA auto target +" +getvalue_func[$key]=getvalue_pgastat + +key=cputime +global_attrs[$key]=" + graph_title Oracle$db_name CPU Time + graph_category db + graph_args --base 1000 --lower-limit 0 --rigid + graph_vlabel seconds + graph_info Oracle CPU Time +" +data_attrs[$key]=" + db_time DERIVE LINE2 DB time + db_cpu DERIVE LINE2 DB CPU + background_elapsed_time DERIVE LINE2 background elapsed time + background_cpu_time DERIVE LINE2 background cpu time + connection_management_call_elapsed_time DERIVE LINE connection management call elapsed time + sequence_load_elapsed_time DERIVE LINE sequence load elapsed time + sql_execute_elapsed_time DERIVE LINE sql execute elapsed time + parse_time_elapsed DERIVE LINE parse time elapsed + hard_parse_elapsed_time DERIVE LINE hard parse elapsed time + hard_parse_sharing_criteria_elapsed_time DERIVE LINE hard parse (sharing criteria) elapsed time + hard_parse_bind_mismatch_elapsed_time DERIVE LINE hard parse (bind mismatch) elapsed time + failed_parse_elapsed_time DERIVE LINE failed parse elapsed time + failed_parse_out_of_shared_memory_elapsed_time DERIVE LINE failed parse (out of shared memory) elapsed time + pl_sql_execution_elapsed_time DERIVE LINE PL/SQL execution elapsed time + inbound_pl_sql_rpc_elapsed_time DERIVE LINE inbound PL/SQL rpc elapsed time + pl_sql_compilation_elapsed_time DERIVE LINE PL/SQL compilation elapsed time + java_execution_elapsed_time DERIVE LINE Java execution elapsed time + repeated_bind_elapsed_time DERIVE LINE repeated bind elapsed time + rman_cpu_time_backup_restore DERIVE LINE RMAN cpu time (backup/restore) +" +getvalue_func[$key]=getvalue_cputime + +key=cachehit +global_attrs[$key]=" + graph_title Oracle$db_name Cache Hit Ratio + graph_category db + graph_args --base 1000 --lower-limit 0 --upper-limit 100 --rigid + graph_vlabel % + graph_info Oracle Cache Hit Ratio - The graph shows cache hit ratio between munin-update intervals (5 minutes in most cases). + graph_scale no + + buf_hitratio.cdef 100,1,buf_physical,buf_logical,/,-,* + lib_hitratio.cdef 100,1,lib_reloads,lib_pins,/,-,* + dict_hitratio.cdef 100,dict_gets,dict_getmisses,-,dict_gets,/,* +" + # Note: + # buf_hitratio = 1 - physical_reads / ( db_block_gets + consistent_gets ) + # lib_hitratio = 1 - reloads / pins + # dict_hitratio = ( gets - misses ) / gets +data_attrs[$key]=" + buf_physical DERIVE LINE dummy + buf_logical DERIVE LINE dummy + lib_pins DERIVE LINE dummy + lib_reloads DERIVE LINE dummy + dict_gets DERIVE LINE dummy + dict_getmisses DERIVE LINE dummy + buf_hitratio GAUGE LINE Buffer Cache Hit Ratio + lib_hitratio GAUGE LINE Library Cache Hit Ratio + dict_hitratio GAUGE LINE Dictionary Cache Hit Ratio +" +getvalue_func[$key]=getvalue_cachehit + +key=sessionuser +global_attrs[$key]=" + graph_title Oracle$db_name Session Users + graph_category db + graph_args --base 1000 --lower-limit 0 --rigid + graph_vlabel count + graph_info Oracle Session Users +" +data_attrs[$key]="" +getfield_func[$key]=getfield_sessionuser +getvalue_func[$key]=getvalue_sessionuser + +key=sessionwait +global_attrs[$key]=" + graph_title Oracle$db_name Session Wait + graph_category db + graph_args --base 1000 --lower-limit 0 --rigid + graph_vlabel count + graph_info Oracle Session Wait +" +data_attrs[$key]="" +getfield_func[$key]=getfield_sessionwait +getvalue_func[$key]=getvalue_sessionwait + +key=eventwait +global_attrs[$key]=" + graph_title Oracle$db_name Wait Events + graph_category db + graph_args --base 1000 --lower-limit 0 --rigid + graph_vlabel microseconds + graph_info Oracle Wait Events - It may look wierd that Y-axis indicates 'microseconds per second'. Although number of times of wait event looks easier to understand, in many cases the number of events does not matter, but wait time become more important to analyze bottle necks. +" +data_attrs[$key]="" +getfield_func[$key]=getfield_eventwait +getvalue_func[$key]=getvalue_eventwait + +key=eventwaitapplication +global_attrs[$key]=" + graph_title Oracle$db_name Wait Events Application + graph_category db + graph_args --base 1000 --lower-limit 0 --rigid + graph_vlabel microseconds + graph_info Oracle Wait Events Application +" +data_attrs[$key]="" +getfield_func[$key]="getfield_eventwait2 Application" +getvalue_func[$key]="getvalue_eventwait2 Application" + +key=eventwaitnetwork +global_attrs[$key]=" + graph_title Oracle$db_name Wait Events Network + graph_category db + graph_args --base 1000 --lower-limit 0 --rigid + graph_vlabel microseconds + graph_info Oracle Wait Events Network +" +data_attrs[$key]="" +getfield_func[$key]="getfield_eventwait2 Network" +getvalue_func[$key]="getvalue_eventwait2 Network" + +key=eventwaitconcurrency +global_attrs[$key]=" + graph_title Oracle$db_name Wait Events Concurrency + graph_category db + graph_args --base 1000 --lower-limit 0 --rigid + graph_vlabel microseconds + graph_info Oracle Wait Events Concurrency +" +data_attrs[$key]="" +getfield_func[$key]="getfield_eventwait2 Concurrency" +getvalue_func[$key]="getvalue_eventwait2 Concurrency" + +key=eventwaituserio +global_attrs[$key]=" + graph_title Oracle$db_name Wait Events User I/O + graph_category db + graph_args --base 1000 --lower-limit 0 --rigid + graph_vlabel microseconds + graph_info Oracle Wait Events User I/O +" +data_attrs[$key]="" +getfield_func[$key]="getfield_eventwait2 User I/O" +getvalue_func[$key]="getvalue_eventwait2 User I/O" + +key=eventwaitsystemio +global_attrs[$key]=" + graph_title Oracle$db_name Wait Events System I/O + graph_category db + graph_args --base 1000 --lower-limit 0 --rigid + graph_vlabel microseconds + graph_info Oracle Wait Events System I/O +" +data_attrs[$key]="" +getfield_func[$key]="getfield_eventwait2 System I/O" +getvalue_func[$key]="getvalue_eventwait2 System I/O" + +key=eventwaitcluster +global_attrs[$key]=" + graph_title Oracle$db_name Wait Events Cluster + graph_category db + graph_args --base 1000 --lower-limit 0 --rigid + graph_vlabel microseconds + graph_info Oracle Wait Events Cluster +" +data_attrs[$key]="" +getfield_func[$key]="getfield_eventwait2 Cluster" +getvalue_func[$key]="getvalue_eventwait2 Cluster" + +key=eventwaitadministrative +global_attrs[$key]=" + graph_title Oracle$db_name Wait Events Administrative + graph_category db + graph_args --base 1000 --lower-limit 0 --rigid + graph_vlabel microseconds + graph_info Oracle Wait Events Administrative +" +data_attrs[$key]="" +getfield_func[$key]="getfield_eventwait2 Administrative" +getvalue_func[$key]="getvalue_eventwait2 Administrative" + +key=eventwaitconfiguration +global_attrs[$key]=" + graph_title Oracle$db_name Wait Events Configuration + graph_category db + graph_args --base 1000 --lower-limit 0 --rigid + graph_vlabel microseconds + graph_info Oracle Wait Events Configuration +" +data_attrs[$key]="" +getfield_func[$key]="getfield_eventwait2 Configuration" +getvalue_func[$key]="getvalue_eventwait2 Configuration" + +key=tablespace +global_attrs[$key]=" + graph_title Oracle$db_name Table Space Usage + graph_category db + graph_args --base 1000 --lower-limit 0 --upper-limit 100 --rigid + graph_vlabel % + graph_info Oracle Table Space Usage + warning ${warning:=92} + critical ${critical:=98} +" +data_attrs[$key]="" +getfield_func[$key]=getfield_tablespace +getvalue_func[$key]=getvalue_tablespace + +key=asmusage +global_attrs[$key]=" + graph_title Oracle$db_name ASM Disk Group Usage + graph_category db + graph_args --base 1000 --lower-limit 0 --upper-limit 100 --rigid + graph_vlabel % + graph_info Oracle ASM Disk Group Usage + warning ${warning:=92} + critical ${critical:=98} +" +data_attrs[$key]="" +getfield_func[$key]=getfield_asmusage +getvalue_func[$key]=getvalue_asmusage + +# End of Graph Settings + +# sqlplus options +: "${sqlplus:=sqlplus -S -L}" +sqlplus_variables=" + whenever sqlerror exit sql.sqlcode + set pagesize 0 + set feed off + set head off + set linesize 256 + set numwidth 30 +" + +# Functions + +autoconf() { + if which sqlplus >/dev/null ; then + echo yes + else + echo "no (failed to find executable 'sqlplus')" + fi +} + +config() { + for module in $( module_list ) + do + do_config + done +} + +fetch() { + for module in $( module_list ) + do + do_fetch + done +} + +do_config() { + local label_max_length=45 + local field type draw label + local fields= + + getfield + echo "multigraph ${plugin_name}_${module}" + + # print global attributes + echo "${global_attrs[$module]}" | sed -e 's/^ *//' -e '/^$/d' + + # print data source attributes + # split line into field,type,draw,label + while read -r field type draw label + do + [ -z "$field" ] && continue + fields="${fields} ${field}" + + echo "${field}.type ${type}" + echo "${field}.draw ${draw}" + echo "${field}.label ${label:0:${label_max_length}}" + if [ "$type" = 'DERIVE' ]; then + echo "${field}.min 0" + fi + if [ "$label" = 'dummy' ]; then + echo "${field}.graph no" + fi + done <<< "${data_attrs[$module]}" + + echo graph_order "$fields" + echo +} + +do_fetch() { + echo "multigraph ${plugin_name}_${module}" + getvalue + echo +} + +module_list() { + local i + + if [ -n "$include_module" ]; then + echo "$include_module" + else + for i in $exclude_module + do + # remove excluded modules + unset -v "global_attrs[$i]" + done + + # print hash keys as available module names + echo "${!global_attrs[@]}" + fi +} + +# wrapper for getfield_* +getfield() { + local func arg + if [ -n "${getfield_func[$module]:-}" ]; then + # call getfield_* function with argument if necessary + read -r func arg <<< "${getfield_func[$module]}" + $func "$arg" + fi +} + +# wrapper for getvalue_* +getvalue() { + local func arg + # call getvalue_* function with argument if necessary + read -r func arg <<< "${getvalue_func[$module]}" + $func "$arg" +} + +getvalue_sysstat() { + local field type draw label + while read -r field type draw label + do + [ -z "$field" ] && continue + + echo "${sqlplus_variables} + VAR vf VARCHAR2(64) + VAR vl VARCHAR2(64) + EXEC :vf := '${field}' + EXEC :vl := '${label}' + SELECT + :vf || '.value ' || value + FROM + v\$sysstat + WHERE + name = :vl; + " + done <<< "${data_attrs[$module]}" | ${sqlplus} "${oracle_auth}" +} + +getvalue_sgainfo() { + local field type draw label + while read -r field type draw label + do + [ -z "$field" ] && continue + + echo "${sqlplus_variables} + VAR vf VARCHAR2(64) + VAR vl VARCHAR2(64) + EXEC :vf := '${field}' + EXEC :vl := '${label}' + SELECT + :vf || '.value ' || bytes + FROM + v\$sgainfo + WHERE + name = :vl; + " + done <<< "${data_attrs[$module]}" | ${sqlplus} "${oracle_auth}" +} + +getvalue_pgastat() { + local field type draw label + while read -r field type draw label + do + [ -z "$field" ] && continue + + echo "${sqlplus_variables} + VAR vf VARCHAR2(64) + VAR vl VARCHAR2(64) + EXEC :vf := '${field}' + EXEC :vl := '${label}' + SELECT + :vf || '.value ' || value + FROM + v\$pgastat + WHERE + name = :vl; + " + done <<< "${data_attrs[$module]}" | ${sqlplus} "${oracle_auth}" +} + +getvalue_cputime() { + local field type draw label + while read -r field type draw label + do + [ -z "$field" ] && continue + + echo "${sqlplus_variables} + VAR vf VARCHAR2(64) + VAR vl VARCHAR2(64) + EXEC :vf := '${field}' + EXEC :vl := '${label}' + SELECT + :vf || '.value ' || ROUND( value / 1000000 ) + FROM + v\$sys_time_model + WHERE + stat_name = :vl; + " + done <<< "${data_attrs[$module]}" | ${sqlplus} "${oracle_auth}" +} + +getvalue_cachehit() { + ${sqlplus} "${oracle_auth}" < count.""" - - out = {} - - def visitor(arg, dirname, names): - for file in names: - if not isfile(join(dirname, file)): - continue - ext = file.split(".")[-1] - - out[ext] = out.get(ext, 0) + 1 - - walk('/var/cache/approx/', visitor, None) - - return out - - -# Autoconfiguration -if len(argv) > 1: - - if argv[1] == "autoconf": - # Test if we can find a approx cache - if exists('/var/cache/approx'): - print "yes" - else: - print "no ('/var/cacne/approx' not found)" - exit(1) - exit() - - elif argv[1] == "config": - print "graph_title Approx cache"; - print "graph yes"; - #print "graph_category Other"; - #print "graph_total Total"; - print "graph_info Statistics from the Approx cache."; - #print "debs.label DEBs"; - #print "pending.warning 0:0"; - #print "hold.label hold"; - for type in get_file_types().keys(): - print "%s.label %s" % (type.lower(), type) - exit() - -for type, count in get_file_types().iteritems(): - print "%s.value %d" % (type.lower(), count) - -exit() diff --git a/plugins/other/freebsd-upgrades b/plugins/other/freebsd-upgrades index e9b0b5a5..313c1950 100755 --- a/plugins/other/freebsd-upgrades +++ b/plugins/other/freebsd-upgrades @@ -2,6 +2,7 @@ if [ "$1" = "config" ]; then echo "graph_title Available Updates" + echo "graph_category security" echo "graph_args --base 1000 -l 0" echo "graph_vlabel upgradeable packages/ports " echo "pkg.label binary packages" diff --git a/plugins/other/ksm_ b/plugins/other/ksm_ index 17442d78..f5ecdf1e 100755 --- a/plugins/other/ksm_ +++ b/plugins/other/ksm_ @@ -19,67 +19,72 @@ # Magic markers - optional - used by installation scripts and # munin-config: # -#%# capabilities=autoconf suggest -#%# family=auto - -########################################################### -category = 'system' # 'upgrades' -title = 'Kernel Samepage Merging' # 'Upgradeable packages' -########################################################### +# #%# capabilities=autoconf suggest +# #%# family=auto import os import sys -import warnings +import warnings # noqa + +################################# +title = 'Kernel Samepage Merging' +################################# + def autoconf(): if os.path.exists('/sys/kernel/mm/ksm/run'): for line in open('/sys/kernel/mm/ksm/run'): if line.strip() == '1': - print 'yes' - sys.exit(0) - print 'no (/sys/kernel/mm/ksm/run must be present and value must be 1)' + print('yes') + break + else: + print('no (/sys/kernel/mm/ksm/run does not contain "1")') + else: + print('no (/sys/kernel/mm/ksm/run not found)') sys.exit(0) + def suggest(): - print 'pages_absolute' - print 'pages_relative' - print 'full_scans' + print('pages_absolute') + print('pages_relative') + print('full_scans') sys.exit(0) def config(): - if('ksm_pages_absolute' in sys.argv[0]): - print 'graph_category %s' % (category) - print 'graph_title %s Pages Absolute' % (title) - print 'graph_order pages_unshared pages_volatile pages_shared pages_sharing' - print 'pages_shared.info how many shared pages are being used' - print 'pages_sharing.info how many more sites are sharing them i.e. how much saved' - print 'pages_unshared.info how many pages unique but repeatedly checked for merging' - print 'pages_volatile.info how many pages changing too fast to be placed in a tree' - print 'pages_shared.label pages_shared' - print 'pages_sharing.label pages_sharing' - print 'pages_unshared.label pages_unshared' - print 'pages_volatile.label pages_volatile' - print 'pages_shared.draw AREASTACK' - print 'pages_sharing.draw AREASTACK' - print 'pages_unshared.draw AREASTACK' - print 'pages_volatile.draw AREASTACK' + if('ksm_pages_absolute' in sys.argv[0]): + print('graph_category system') + print('graph_title %s Pages Absolute' % (title)) + print('graph_order pages_unshared pages_volatile pages_shared pages_sharing') + print('pages_shared.info how many shared pages are being used') + print('pages_sharing.info how many more sites are sharing them i.e. how much saved') + print('pages_unshared.info how many pages unique but repeatedly checked for merging') + print('pages_volatile.info how many pages changing too fast to be placed in a tree') + print('pages_shared.label pages_shared') + print('pages_sharing.label pages_sharing') + print('pages_unshared.label pages_unshared') + print('pages_volatile.label pages_volatile') + print('pages_shared.draw AREASTACK') + print('pages_sharing.draw AREASTACK') + print('pages_unshared.draw AREASTACK') + print('pages_volatile.draw AREASTACK') elif('ksm_pages_relative' in sys.argv[0]): - print 'graph_category %s' % (category) - print 'graph_title %s Pages Relative' % (title) - print 'pages_sharing_shared.info ratio of sharing to shared pages' - print 'pages_unshared_sharing.info ratio of unshared to sharing pages' - print 'pages_sharing_shared.label pages_sharing_shared' - print 'pages_unshared_sharing.label pages_unshared_sharing' - print 'pages_sharing_shared.cdef pages_sharing_shared,100,*' - print 'pages_unshared_sharing.cdef pages_unshared_sharing,100,*' + print('graph_category system') + print('graph_title %s Pages Relative' % (title)) + print('pages_sharing_shared.info ratio of sharing to shared pages') + print('pages_unshared_sharing.info ratio of unshared to sharing pages') + print('pages_sharing_shared.label pages_sharing_shared') + print('pages_unshared_sharing.label pages_unshared_sharing') + print('pages_sharing_shared.cdef pages_sharing_shared,100,*') + print('pages_unshared_sharing.cdef pages_unshared_sharing,100,*') elif('ksm_full_scans' in sys.argv[0]): - print 'graph_category %s' % (category) - print 'graph_title %s Full Scans' % (title) - print 'full_scans.info how many times all mergeable areas have been scanned' - print 'full_scans.label full_scans' + print('graph_category system') + print('graph_title %s Full Scans' % (title)) + print('full_scans.info how many times all mergeable areas have been scanned') + print('full_scans.label full_scans') sys.exit(0) + if len(sys.argv) > 1: if sys.argv[1] == 'autoconf': autoconf() @@ -91,19 +96,21 @@ if len(sys.argv) > 1: print('unknown argument "' + sys.argv[1] + '"') sys.exit(1) -pages_shared=int(open('/sys/kernel/mm/ksm/pages_shared').read()) -pages_sharing=int(open('/sys/kernel/mm/ksm/pages_sharing').read()) -pages_unshared=int(open('/sys/kernel/mm/ksm/pages_unshared').read()) -pages_volatile=int(open('/sys/kernel/mm/ksm/pages_volatile').read()) -full_scans=int(open('/sys/kernel/mm/ksm/full_scans').read()) +pages_shared = int(open('/sys/kernel/mm/ksm/pages_shared').read()) +pages_sharing = int(open('/sys/kernel/mm/ksm/pages_sharing').read()) +pages_unshared = int(open('/sys/kernel/mm/ksm/pages_unshared').read()) +pages_volatile = int(open('/sys/kernel/mm/ksm/pages_volatile').read()) +full_scans = int(open('/sys/kernel/mm/ksm/full_scans').read()) -if('ksm_pages_absolute' in sys.argv[0]): - print 'pages_shared.value %i' % pages_shared - print 'pages_sharing.value %i' % pages_sharing - print 'pages_unshared.value %i' % pages_unshared - print 'pages_volatile.value %i' % pages_volatile -elif('ksm_pages_relative' in sys.argv[0]): - print 'pages_sharing_shared.value %f' % (float(pages_sharing)/float(pages_shared) if pages_shared>0 else 0) - print 'pages_unshared_sharing.value %f' % (float(pages_unshared)/float(pages_sharing) if pages_sharing>0 else 0) -elif('ksm_full_scans' in sys.argv[0]): - print 'full_scans.value %i' % full_scans +if('ksm_pages_absolute' in sys.argv[0]): + print('pages_shared.value %i' % pages_shared) + print('pages_sharing.value %i' % pages_sharing) + print('pages_unshared.value %i' % pages_unshared) + print('pages_volatile.value %i' % pages_volatile) +elif('ksm_pages_relative' in sys.argv[0]): + print('pages_sharing_shared.value %f' + % (float(pages_sharing) / float(pages_shared) if pages_shared > 0 else 0)) + print('pages_unshared_sharing.value %f' + % (float(pages_unshared) / float(pages_sharing) if pages_sharing > 0 else 0)) +elif('ksm_full_scans' in sys.argv[0]): + print('full_scans.value %i' % full_scans) diff --git a/plugins/other/listeners b/plugins/other/listeners index 44d9fa7a..8f426d36 100755 --- a/plugins/other/listeners +++ b/plugins/other/listeners @@ -116,3 +116,5 @@ run_() { # plugin entry point: run_$1 +# for Munin Plugin Gallery +# graph_category streaming diff --git a/plugins/other/opentracker_ b/plugins/other/opentracker_ index 0bfe0f78..ae424bda 100755 --- a/plugins/other/opentracker_ +++ b/plugins/other/opentracker_ @@ -86,7 +86,7 @@ $graphs{conn} = { config => { args => '--lower-limit 0', vlabel => 'Connections', - category => 'opentracker', + category => 'filetransfer', title => 'Current Connections', info => 'Current Connections to OpenTracker', }, diff --git a/plugins/other/pacman_pending_updates b/plugins/other/pacman_pending_updates new file mode 100755 index 00000000..c5e66b93 --- /dev/null +++ b/plugins/other/pacman_pending_updates @@ -0,0 +1,67 @@ +#!/bin/bash + +: <<=cut + +=head1 NAME + +pacman_pending_updates - Plugin to monitor pending updates + +=head1 APPLICABLE SYSTEMS + +All systems with pacman as their package manager. + +=head1 CONFIGURATION + +The plugin needs no additional configuration and works out of the box. + +=head1 INTERPRETATION + +This plugin will draw one line: the number of updates pending. + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=head1 VERSION + + 1.1.0 + +=head1 AUTHOR + +Bert Peters + +=head1 LICENSE + +GPLv2 + +=cut + +case $1 in + config) + cat <<'EOM' +graph_args --base 1000 -l 0 +graph_title Pending updates +graph_vlabel updates +graph_category security +updates.label updates +updates.info Current number of pending updates +EOM + ;; + + autoconf) + hash checkupdates &> /dev/null && echo yes || echo "no (checkupdates not found)" + ;; + + *) + updates="$(checkupdates)" + if [ -n "$updates" ]; then + echo "updates.value $(echo "$updates" | wc -l)" + echo "updates.extinfo $(echo "$updates" | paste -s -d,)" + else + echo updates.value 0 + fi + ;; +esac + +exit 0 diff --git a/plugins/other/update b/plugins/other/update index 0a6b36a1..c86bce9c 100755 --- a/plugins/other/update +++ b/plugins/other/update @@ -50,6 +50,7 @@ fi if [ "$1" = "config" ]; then echo 'graph_title Munin-update' + echo 'graph_category munin' echo 'graph_vlabel seconds' [ -f $UPDATE_STATSFILE ] || exit 0 cat $UPDATE_STATSFILE | grep "UD" | while read i; do diff --git a/plugins/other/passenger_memory b/plugins/passenger/passenger_memory similarity index 96% rename from plugins/other/passenger_memory rename to plugins/passenger/passenger_memory index a4237458..b63a361c 100755 --- a/plugins/other/passenger_memory +++ b/plugins/passenger/passenger_memory @@ -12,6 +12,7 @@ memory_stats_command = ENV['memory_stats_command'] || '/opt/ruby-enterprise-1.8. if ARGV.length > 0 && ARGV[0] == 'config' puts "graph_title Passenger Memory Usage" + puts "graph_category webserver" puts "graph_vlabel MB" puts "apache_rss.label Apache Dirty RSS" puts "passenger_rss.label Passenger Dirty RSS" diff --git a/plugins/other/passenger_processes b/plugins/passenger/passenger_processes similarity index 96% rename from plugins/other/passenger_processes rename to plugins/passenger/passenger_processes index 9b68a3d0..d8097e96 100755 --- a/plugins/other/passenger_processes +++ b/plugins/passenger/passenger_processes @@ -9,6 +9,7 @@ process_stats_command = ENV['process_stats_command'] || '/opt/ruby-enterprise-1. if ARGV.length > 0 && ARGV[0] == 'config' puts "graph_title Passenger Processes" + puts "graph_category webserver" puts "graph_vlabel processes" puts "max.label Max processes" puts "count.label Total processes" diff --git a/plugins/passenger/passenger_status b/plugins/passenger/passenger_status index 5fd24419..cd3e2a17 100755 --- a/plugins/passenger/passenger_status +++ b/plugins/passenger/passenger_status @@ -2,7 +2,7 @@ def output_config puts <<-END -graph_category passenger +graph_category webserver graph_title status graph_vlabel count graph_info This graph shows how much passenger process are working, available and how much queries are waiting. @@ -52,4 +52,4 @@ if ARGV[0] == "config" output_config else output_values -end \ No newline at end of file +end diff --git a/plugins/network/dns/pdns_errors b/plugins/pdns/pdns_errors similarity index 75% rename from plugins/network/dns/pdns_errors rename to plugins/pdns/pdns_errors index 19bf66ac..b971860e 100755 --- a/plugins/network/dns/pdns_errors +++ b/plugins/pdns/pdns_errors @@ -9,18 +9,24 @@ #%# family=auto #%# capabilities=autoconf -command="/etc/init.d/pdns dump" +pdns_control="/usr/bin/pdns_control" +command="$pdns_control list" if [ "$1" = "autoconf" ]; then - echo yes - exit 0 + if [ -e "$pdns_control" ]; then + echo yes + exit 0 + else + echo "no (missing $pdns_control)" + exit 0 + fi fi if [ "$1" = "config" ]; then echo 'graph_title Power DNS errors' echo 'graph_args -l 0 --base 1000' echo 'graph_vlabel numbers of' - echo 'graph_category Power DNS' + echo 'graph_category dns' echo 'graph_info This graph shows Power DNS performance on the machine.' echo 'corrupt_packets.label corrupt packets' echo 'corrupt_packets.type DERIVE' @@ -38,4 +44,4 @@ if [ "$1" = "config" ]; then fi -$command | sed 's/=\([0-9]\+\),/.value \1\n/g' | grep corrupt'\|'servfail'\|'timedout | sed 's/-/_/g' +$command | sed 's/=\([0-9]\+\),/.value \1\n/g' | egrep "corrupt|servfail|timedout" | sed 's/-/_/g' diff --git a/plugins/network/dns/pdns_latency b/plugins/pdns/pdns_latency similarity index 66% rename from plugins/network/dns/pdns_latency rename to plugins/pdns/pdns_latency index 9398d6d5..47753079 100755 --- a/plugins/network/dns/pdns_latency +++ b/plugins/pdns/pdns_latency @@ -9,18 +9,24 @@ #%# family=auto #%# capabilities=autoconf -command="/etc/init.d/pdns show" +pdns_control="/usr/bin/pdns_control" +command="$pdns_control show" if [ "$1" = "autoconf" ]; then - echo yes - exit 0 + if [ -e "$pdns_control" ]; then + echo yes + exit 0 + else + echo "no (missing $pdns_control)" + exit 0 + fi fi if [ "$1" = "config" ]; then echo 'graph_title Power DNS latency' echo 'graph_args -l 0' echo 'graph_vlabel usec' - echo 'graph_category Power DNS' + echo 'graph_category dns' echo 'graph_info This graph shows Power DNS latency on the machine.' echo 'latency.label latency' echo 'latency.info Average number of microseconds needed to answer a question' @@ -31,4 +37,4 @@ fi -echo "latency.value $($command latency | awk -F= '{print $2}')" +echo "latency.value $($command latency)" diff --git a/plugins/network/dns/pdns_qsize b/plugins/pdns/pdns_qsize similarity index 67% rename from plugins/network/dns/pdns_qsize rename to plugins/pdns/pdns_qsize index d818fea8..4d0a86c7 100755 --- a/plugins/network/dns/pdns_qsize +++ b/plugins/pdns/pdns_qsize @@ -9,18 +9,24 @@ #%# family=auto #%# capabilities=autoconf -command="/etc/init.d/pdns show" +pdns_control="/usr/bin/pdns_control" +command="$pdns_control show" if [ "$1" = "autoconf" ]; then - echo yes - exit 0 + if [ -e "$pdns_control" ]; then + echo yes + exit 0 + else + echo "no (missing $pdns_control)" + exit 0 + fi fi if [ "$1" = "config" ]; then echo 'graph_title Power DNS database queue' echo 'graph_args -l 0' echo 'graph_vlabel number of waiting queries' - echo 'graph_category Power DNS' + echo 'graph_category dns' echo 'graph_info This graph shows Power DNS database performance on the machine.' echo 'qsize.label qsize' echo 'qsize.info Number of questions waiting for database attention' @@ -31,5 +37,5 @@ fi -echo "qsize.value $($command qsize_q | awk -F= '{print $2}')" +echo "qsize.value $($command qsize-q)" diff --git a/plugins/network/dns/pdns_queries b/plugins/pdns/pdns_queries similarity index 81% rename from plugins/network/dns/pdns_queries rename to plugins/pdns/pdns_queries index 8c9f4ea9..edad5810 100755 --- a/plugins/network/dns/pdns_queries +++ b/plugins/pdns/pdns_queries @@ -9,18 +9,24 @@ #%# family=auto #%# capabilities=autoconf -command="/etc/init.d/pdns dump" +pdns_control="/usr/bin/pdns_control" +command="/usr/bin/pdns_control list" if [ "$1" = "autoconf" ]; then - echo yes - exit 0 + if [ -e "$pdns_control" ]; then + echo yes + exit 0 + else + echo "no (missing $pdns_control)" + exit 0 + fi fi if [ "$1" = "config" ]; then echo 'graph_title Power DNS queries' echo 'graph_args -l 0 --base 1000' echo 'graph_vlabel numbers of' - echo 'graph_category Power DNS' + echo 'graph_category dns' echo 'graph_info This graph shows Power DNS performance on the machine.' echo 'recursing_answers.label recursing answers' echo 'recursing_answers.type DERIVE' @@ -50,4 +56,4 @@ if [ "$1" = "config" ]; then fi -$command | sed 's/=\([0-9]\+\),/.value \1\n/g' | grep udp-'\|'recursing'\|'tcp | sed 's/-/_/g' +$command | sed 's/=\([0-9]\+\),/.value \1\n/g' | egrep "udp-|recursing|tcp" | sed 's/-/_/g' diff --git a/plugins/network/dns/pdns_rec_answers b/plugins/pdns/pdns_rec_answers similarity index 75% rename from plugins/network/dns/pdns_rec_answers rename to plugins/pdns/pdns_rec_answers index 82f14161..a7db89f6 100755 --- a/plugins/network/dns/pdns_rec_answers +++ b/plugins/pdns/pdns_rec_answers @@ -9,13 +9,15 @@ # echo '[pdns_rec_*]' >/etc/munin/plugin-conf.d/pdns_rec # echo 'user root' >>/etc/munin/plugin-conf.d/pdns_rec +rec_control="/usr/bin/rec_control" + if [ "$1" = "autoconf" ]; then - if [ -e /usr/bin/rec_control ]; then + if [ -e "$rec_control" ]; then echo yes exit 0 else - echo no - exit 1 + echo "no (missing $rec_control)" + exit 0 fi fi @@ -24,7 +26,7 @@ if [ "$1" = "config" ]; then echo 'graph_order a b c d e f' echo 'graph_vlabel queries' echo 'graph_info Time required per answer.' - echo 'graph_category pdns' + echo 'graph_category dns' echo 'a.label in 1ms' echo 'a.min 0' @@ -65,11 +67,11 @@ if [ "$1" = "config" ]; then exit 0 fi -echo a.value `rec_control get answers0-1` -echo b.value `rec_control get answers1-10` -echo c.value `rec_control get answers10-100` -echo d.value `rec_control get answers100-1000` -echo e.value `rec_control get answers-slow` -echo f.value `rec_control get outgoing-timeouts` +echo a.value "$($rec_control get answers0-1)" +echo b.value "$($rec_control get answers1-10)" +echo c.value "$($rec_control get answers10-100)" +echo d.value "$($rec_control get answers100-1000)" +echo e.value "$($rec_control get answers-slow)" +echo f.value "$($rec_control get outgoing-timeouts)" exit 0 diff --git a/plugins/network/dns/pdns_rec_cache b/plugins/pdns/pdns_rec_cache similarity index 73% rename from plugins/network/dns/pdns_rec_cache rename to plugins/pdns/pdns_rec_cache index b6f44216..007ad4cd 100755 --- a/plugins/network/dns/pdns_rec_cache +++ b/plugins/pdns/pdns_rec_cache @@ -9,17 +9,19 @@ # echo '[pdns_rec_*]' >/etc/munin/plugin-conf.d/pdns_rec # echo 'user root' >>/etc/munin/plugin-conf.d/pdns_rec +rec_control="/usr/bin/rec_control" + if [ "$1" = "autoconf" ]; then - if [ -e /usr/bin/rec_control ]; then + if [ -e "$rec_control" ]; then echo yes exit 0 else - echo no - exit 1 + echo "no (missing $rec_control)" + exit 0 fi fi -RESENDS=`rec_control get cache-resends` +RESENDS="$($rec_control get cache-resends)" ISRESENDS="" [ "$RESENDS" != "UNKNOWN" ] && ISRESENDS="resends" @@ -28,7 +30,7 @@ if [ "$1" = "config" ]; then echo "graph_order hits misses $ISRESENDS" echo 'graph_vlabel entries' echo 'graph_info Hit/miss rate' - echo 'graph_category pdns' + echo 'graph_category dns' echo 'hits.label hits' echo 'hits.min 0' @@ -53,8 +55,8 @@ if [ "$1" = "config" ]; then exit 0 fi -echo hits.value `rec_control get cache-hits` -echo misses.value `rec_control get cache-misses` -[ "$RESENDS" != "UNKNOWN" ] && echo resends.value `rec_control get cache-resends` +echo hits.value "$($rec_control get cache-hits)" +echo misses.value "$($rec_control get cache-misses)" +[ "$RESENDS" != "UNKNOWN" ] && echo resends.value "$($rec_control get cache-resends)" exit 0 diff --git a/plugins/network/dns/pdns_rec_cache_size b/plugins/pdns/pdns_rec_cache_size similarity index 75% rename from plugins/network/dns/pdns_rec_cache_size rename to plugins/pdns/pdns_rec_cache_size index bab0ec46..cec24d94 100755 --- a/plugins/network/dns/pdns_rec_cache_size +++ b/plugins/pdns/pdns_rec_cache_size @@ -9,13 +9,15 @@ # echo '[pdns_rec_*]' >/etc/munin/plugin-conf.d/pdns_rec # echo 'user root' >>/etc/munin/plugin-conf.d/pdns_rec +rec_control="/usr/bin/rec_control" + if [ "$1" = "autoconf" ]; then - if [ -e /usr/bin/rec_control ]; then + if [ -e "$rec_control" ]; then echo yes exit 0 else - echo no - exit 1 + echo "no (missing $rec_control)" + exit 0 fi fi @@ -24,7 +26,7 @@ if [ "$1" = "config" ]; then echo 'graph_order entries negative' echo 'graph_vlabel entries' echo 'graph_info Size of the cache' - echo 'graph_category pdns' + echo 'graph_category dns' echo 'entries.label Entries' echo 'entries.min 0' @@ -39,7 +41,7 @@ if [ "$1" = "config" ]; then exit 0 fi -echo entries.value `rec_control get cache-entries` -echo negative.value `rec_control get negcache-entries` +echo entries.value "$($rec_control get cache-entries)" +echo negative.value "$($rec_control get negcache-entries)" exit 0 diff --git a/plugins/network/dns/pdns_rec_concurrent b/plugins/pdns/pdns_rec_concurrent similarity index 77% rename from plugins/network/dns/pdns_rec_concurrent rename to plugins/pdns/pdns_rec_concurrent index 86eb4ab5..dcba6ad5 100755 --- a/plugins/network/dns/pdns_rec_concurrent +++ b/plugins/pdns/pdns_rec_concurrent @@ -9,13 +9,15 @@ # echo '[pdns_rec_*]' >/etc/munin/plugin-conf.d/pdns_rec # echo 'user root' >>/etc/munin/plugin-conf.d/pdns_rec +rec_control="/usr/bin/rec_control" + if [ "$1" = "autoconf" ]; then - if [ -e /usr/bin/rec_control ]; then + if [ -e "$rec_control" ]; then echo yes exit 0 else - echo no - exit 1 + echo "no (missing $rec_control)" + exit 0 fi fi @@ -24,7 +26,7 @@ if [ "$1" = "config" ]; then echo 'graph_order concurrent' echo 'graph_vlabel queries' echo 'graph_info Concurrent queries' - echo 'graph_category pdns' + echo 'graph_category dns' echo 'concurrent.label queries' echo 'concurrent.min 0' @@ -34,6 +36,6 @@ if [ "$1" = "config" ]; then exit 0 fi -echo concurrent.value `rec_control get concurrent-queries` +echo concurrent.value "$($rec_control get concurrent-queries)" exit 0 diff --git a/plugins/network/dns/pdns_rec_issues b/plugins/pdns/pdns_rec_issues similarity index 75% rename from plugins/network/dns/pdns_rec_issues rename to plugins/pdns/pdns_rec_issues index 9c9cee83..1191bb17 100755 --- a/plugins/network/dns/pdns_rec_issues +++ b/plugins/pdns/pdns_rec_issues @@ -9,13 +9,15 @@ # echo '[pdns_rec_*]' >/etc/munin/plugin-conf.d/pdns_rec # echo 'user root' >>/etc/munin/plugin-conf.d/pdns_rec +rec_control="/usr/bin/rec_control" + if [ "$1" = "autoconf" ]; then - if [ -e /usr/bin/rec_control ]; then + if [ -e "$rec_control" ]; then echo yes exit 0 else - echo no - exit 1 + echo "no (missing $rec_control)" + exit 0 fi fi @@ -24,7 +26,7 @@ if [ "$1" = "config" ]; then echo 'graph_order spoofs resource client server overflow' echo 'graph_vlabel queries' echo 'graph_info Exceptional queries' - echo 'graph_category pdns' + echo 'graph_category dns' echo 'spoofs.label spoofs' echo 'spoofs.min 0' @@ -59,10 +61,10 @@ if [ "$1" = "config" ]; then exit 0 fi -echo spoofs.value `rec_control get spoof-prevents` -echo resource.value `rec_control get resource-limits` -echo client.value `rec_control get client-parse-errors` -echo server.value `rec_control get server-parse-errors` -echo overflow.value `rec_control get tcp-client-overflow` +echo spoofs.value "$($rec_control get spoof-prevents)" +echo resource.value "$($rec_control get resource-limits)" +echo client.value "$($rec_control get client-parse-errors)" +echo server.value "$($rec_control get server-parse-errors)" +echo overflow.value "$($rec_control get tcp-client-overflow)" exit 0 diff --git a/plugins/network/dns/pdns_rec_outqueries b/plugins/pdns/pdns_rec_outqueries similarity index 76% rename from plugins/network/dns/pdns_rec_outqueries rename to plugins/pdns/pdns_rec_outqueries index a9ce3a7e..000c436c 100755 --- a/plugins/network/dns/pdns_rec_outqueries +++ b/plugins/pdns/pdns_rec_outqueries @@ -9,13 +9,15 @@ # echo '[pdns_rec_*]' >/etc/munin/plugin-conf.d/pdns_rec # echo 'user root' >>/etc/munin/plugin-conf.d/pdns_rec +rec_control="/usr/bin/rec_control" + if [ "$1" = "autoconf" ]; then - if [ -e /usr/bin/rec_control ]; then + if [ -e "$rec_control" ]; then echo yes exit 0 else - echo no - exit 1 + echo "no (missing $rec_control)" + exit 0 fi fi @@ -24,7 +26,7 @@ if [ "$1" = "config" ]; then echo 'graph_order all tcp' echo 'graph_vlabel queries' echo 'graph_info Outbound queries' - echo 'graph_category pdns' + echo 'graph_category dns' echo 'all.label all' echo 'all.min 0' @@ -41,7 +43,7 @@ if [ "$1" = "config" ]; then exit 0 fi -echo all.value `rec_control get all-outqueries` -echo tcp.value `rec_control get tcp-outqueries` +echo all.value "$($rec_control get all-outqueries)" +echo tcp.value "$($rec_control get tcp-outqueries)" exit 0 diff --git a/plugins/network/dns/pdns_rec_qtypes b/plugins/pdns/pdns_rec_qtypes similarity index 96% rename from plugins/network/dns/pdns_rec_qtypes rename to plugins/pdns/pdns_rec_qtypes index 03744804..7c966c3e 100755 --- a/plugins/network/dns/pdns_rec_qtypes +++ b/plugins/pdns/pdns_rec_qtypes @@ -9,13 +9,15 @@ # echo '[pdns_rec_*]' >/etc/munin/plugin-conf.d/pdns_rec # echo 'user root' >>/etc/munin/plugin-conf.d/pdns_rec +rec_control="/usr/bin/rec_control" + if [ "$1" = "autoconf" ]; then - if [ -e /usr/bin/rec_control ]; then + if [ -e "$rec_control" ]; then echo yes exit 0 else - echo no - exit 1 + echo "no (missing $rec_control)" + exit 0 fi fi @@ -24,7 +26,7 @@ if [ "$1" = "config" ]; then echo 'graph_order a ns cname soa mr ptr hinfo mx txt rp afsdb sig key aaaa loc srv naptr kx cert a6 ds sshfp ipseckey rrsig nsec dnskey dhcid nsec3 nsec3param tlsa spf eui48 eui64 tsig mailb maila any dlv' echo 'graph_vlabel querytypes' echo 'graph_info Querytype breakdown' - echo 'graph_category pdns' + echo 'graph_category dns' echo 'a.label A queries' echo 'a.min 0' @@ -245,6 +247,6 @@ END { } ' -rec_control get-qtypelist | awk "$awkscript" +"$rec_control" get-qtypelist | awk "$awkscript" -exit 0 \ No newline at end of file +exit 0 diff --git a/plugins/network/dns/pdns_rec_querylatency b/plugins/pdns/pdns_rec_querylatency similarity index 77% rename from plugins/network/dns/pdns_rec_querylatency rename to plugins/pdns/pdns_rec_querylatency index 1c3d1ed4..84de6803 100755 --- a/plugins/network/dns/pdns_rec_querylatency +++ b/plugins/pdns/pdns_rec_querylatency @@ -9,13 +9,15 @@ # echo '[pdns_rec_*]' >/etc/munin/plugin-conf.d/pdns_rec # echo 'user root' >>/etc/munin/plugin-conf.d/pdns_rec +rec_control="/usr/bin/rec_control" + if [ "$1" = "autoconf" ]; then - if [ -e /usr/bin/rec_control ]; then + if [ -e "$rec_control" ]; then echo yes exit 0 else - echo no - exit 1 + echo "no (missing $rec_control)" + exit 0 fi fi @@ -24,7 +26,7 @@ if [ "$1" = "config" ]; then echo 'graph_order latency' echo 'graph_vlabel ms' echo 'graph_info Question latency' - echo 'graph_category pdns' + echo 'graph_category dns' echo 'latency.label ms' echo 'latency.min 0' @@ -34,6 +36,6 @@ if [ "$1" = "config" ]; then exit 0 fi -echo latency.value `rec_control get qa-latency` +echo latency.value "$($rec_control get qa-latency)" exit 0 diff --git a/plugins/network/dns/pdns_rec_questions b/plugins/pdns/pdns_rec_questions similarity index 76% rename from plugins/network/dns/pdns_rec_questions rename to plugins/pdns/pdns_rec_questions index 2e38e67c..f0cfe59c 100755 --- a/plugins/network/dns/pdns_rec_questions +++ b/plugins/pdns/pdns_rec_questions @@ -9,13 +9,15 @@ # echo '[pdns_rec_*]' >/etc/munin/plugin-conf.d/pdns_rec # echo 'user root' >>/etc/munin/plugin-conf.d/pdns_rec +rec_control="/usr/bin/rec_control" + if [ "$1" = "autoconf" ]; then - if [ -e /usr/bin/rec_control ]; then + if [ -e "$rec_control" ]; then echo yes exit 0 else - echo no - exit 1 + echo "no (missing $rec_control)" + exit 0 fi fi @@ -24,7 +26,7 @@ if [ "$1" = "config" ]; then echo 'graph_order all tcp' echo 'graph_vlabel queries' echo 'graph_info Number of questions asked' - echo 'graph_category pdns' + echo 'graph_category dns' echo 'all.label all' echo 'all.min 0' @@ -41,7 +43,7 @@ if [ "$1" = "config" ]; then exit 0 fi -echo all.value `rec_control get questions` -echo tcp.value `rec_control get tcp-questions` +echo all.value "$($rec_control get questions)" +echo tcp.value "$($rec_control get tcp-questions)" exit 0 diff --git a/plugins/network/dns/pdns_rec_throttle b/plugins/pdns/pdns_rec_throttle similarity index 77% rename from plugins/network/dns/pdns_rec_throttle rename to plugins/pdns/pdns_rec_throttle index 8fbd66a9..df6f0758 100755 --- a/plugins/network/dns/pdns_rec_throttle +++ b/plugins/pdns/pdns_rec_throttle @@ -9,13 +9,15 @@ # echo '[pdns_rec_*]' >/etc/munin/plugin-conf.d/pdns_rec # echo 'user root' >>/etc/munin/plugin-conf.d/pdns_rec +rec_control="/usr/bin/rec_control" + if [ "$1" = "autoconf" ]; then - if [ -e /usr/bin/rec_control ]; then + if [ -e "$rec_control" ]; then echo yes exit 0 else - echo no - exit 1 + echo "no (missing $rec_control)" + exit 0 fi fi @@ -24,7 +26,7 @@ if [ "$1" = "config" ]; then echo 'graph_order throttled' echo 'graph_vlabel queries' echo 'graph_info Throttled queries' - echo 'graph_category pdns' + echo 'graph_category dns' echo 'throttled.label throttled' echo 'throttled.min 0' @@ -34,6 +36,6 @@ if [ "$1" = "config" ]; then exit 0 fi -echo throttled.value `rec_control get throttled-out` +echo throttled.value "$($rec_control get throttled-out)" exit 0 diff --git a/plugins/network/dns/pdns_rec_unauth b/plugins/pdns/pdns_rec_unauth similarity index 76% rename from plugins/network/dns/pdns_rec_unauth rename to plugins/pdns/pdns_rec_unauth index b7b82efa..92df5af1 100755 --- a/plugins/network/dns/pdns_rec_unauth +++ b/plugins/pdns/pdns_rec_unauth @@ -9,13 +9,15 @@ # echo '[pdns_rec_*]' >/etc/munin/plugin-conf.d/pdns_rec # echo 'user root' >>/etc/munin/plugin-conf.d/pdns_rec +rec_control="/usr/bin/rec_control" + if [ "$1" = "autoconf" ]; then - if [ -e /usr/bin/rec_control ]; then + if [ -e "$rec_control" ]; then echo yes exit 0 else - echo no - exit 1 + echo "no (missing $rec_control)" + exit 0 fi fi @@ -24,7 +26,7 @@ if [ "$1" = "config" ]; then echo 'graph_order tcp udp unexpected' echo 'graph_vlabel queries' echo 'graph_info Unauthorized requests' - echo 'graph_category pdns' + echo 'graph_category dns' echo 'tcp.label tcp' echo 'tcp.min 0' @@ -47,8 +49,8 @@ if [ "$1" = "config" ]; then exit 0 fi -echo tcp.value `rec_control get unauthorized-tcp` -echo udp.value `rec_control get unauthorized-udp` -echo unexpected.value `rec_control get unexpected-packets` +echo tcp.value "$($rec_control get unauthorized-tcp)" +echo udp.value "$($rec_control get unauthorized-udp)" +echo unexpected.value "$($rec_control get unexpected-packets)" exit 0 diff --git a/plugins/pdns/pdns_rel b/plugins/pdns/pdns_rel new file mode 100755 index 00000000..b42439d8 --- /dev/null +++ b/plugins/pdns/pdns_rel @@ -0,0 +1,56 @@ +#!/bin/bash +# +# Script to monitor PowerDNS performance +# +# Parameters understood: +# +# config (required) +# autoconf (optional - used by munin-config) +#%# family=auto +#%# capabilities=autoconf + +pdns_control="/usr/bin/pdns_control" +command="$pdns_control show" +state_file=$MUNIN_PLUGSTATE/pdns_rel.state + +if [ "$1" = "autoconf" ]; then + if [ -e "$pdns_control" ]; then + echo yes + exit 0 + else + echo "no (missing $pdns_control)" + exit 0 + fi +fi + +if [ "$1" = "config" ]; then + echo 'graph_title Power DNS Packet Cache Performance' + echo 'graph_args -l 0 --upper-limit 100 --base 1000' + echo 'graph_vlabel %' + echo 'graph_category dns' + echo 'graph_info This graph shows the Power DNS packet cache performance on the machine.' + echo 'packetcache_hitrate.label packet cache hitrate' + echo 'packetcache_hitrate.type GAUGE' + echo 'packetcache_hitrate.min 0' + echo 'packetcache_hitrate.max 100' + echo 'packetcache_hitrate.info Hits on the packets cache' + exit 0 +fi + +hits=$($command packetcache-hit) +queries=$($command udp-queries) +if [ -f "$state_file" ]; then + old_hits=$(head -n1 "$state_file") + old_queries=$(tail -n1 "$state_file") +fi + +if [ -f "$state_file" ] && [ "$(stat --format=%Y "$state_file")" -gt "$(date --date="7 minutes ago" +%s)" ] ; then + d_hits=$((hits - old_hits)) + d_queries=$((queries - old_queries)) + if [ $d_queries -gt 0 ] ; then + echo packetcache_hitrate.value $(( d_hits * 100 / d_queries )) + fi +fi + +echo "$hits" > "$state_file" +echo "$queries" >> "$state_file" diff --git a/plugins/percona/percona_ b/plugins/percona/percona_ old mode 100644 new mode 100755 index f06f6e5a..6282f09f --- a/plugins/percona/percona_ +++ b/plugins/percona/percona_ @@ -24,7 +24,8 @@ # This plugin requires pythons MySQLdb module which can be installed via easy_install. # # ## Installation -# Copy file to directory /usr/share/munin/plugins/ and create symbolic links for each type you wish to monitor: +# Copy file to directory /usr/share/munin/plugins/ and create symbolic links for each type you wish +# to monitor: # percona_flow # percona_queues # percona_replication @@ -37,14 +38,19 @@ # env.user root # env.password vErYsEcReT # -#%# capabilities=autoconf -#%# family=contrib +# #%# capabilities=autoconf +# #%# family=contrib +import os +import sys from warnings import filterwarnings -import os, sys, MySQLdb, MySQLdb.cursors -filterwarnings('ignore', category = MySQLdb.Warning) -progName = os.path.basename(__file__) +import MySQLdb +import MySQLdb.cursors + +filterwarnings('ignore', category=MySQLdb.Warning) + +program_name = os.path.basename(__file__) variables = { 'percona_queues': { @@ -76,13 +82,13 @@ variables = { # Parse environment variables # Mysql host -if "host" in os.environ and os.environ["host"] != None: +if "host" in os.environ and os.environ["host"] is not None: server = os.environ["host"] else: - server = "localhost" + server = "localhost" # Mysql port -if "port" in os.environ and os.environ["port"] != None: +if "port" in os.environ and os.environ["port"] is not None: try: port = int(os.environ["port"]) except ValueError: @@ -91,13 +97,13 @@ else: port = 3306 # Mysql username -if "user" in os.environ and os.environ["user"] != None: +if "user" in os.environ and os.environ["user"] is not None: login = os.environ["user"] else: login = "" # Mysql password -if "password" in os.environ and os.environ["password"] != None: +if "password" in os.environ and os.environ["password"] is not None: passw = os.environ["password"] else: passw = "" @@ -105,9 +111,9 @@ else: # Mysql connection handler conn = None -label = variables[progName]['label'] -vlabel = variables[progName]['vlabel'] -fields = ['\'{0}\''.format(x) for x in variables[progName]['fields']] +label = variables[program_name]['label'] +vlabel = variables[program_name]['vlabel'] +fields = ["'{0}'".format(x) for x in variables[program_name]['fields']] query = "show status where Variable_name in (%s)" % ', '.join(fields) @@ -115,31 +121,31 @@ query = "show status where Variable_name in (%s)" % ', '.join(fields) try: conn = MySQLdb.connect(host=server, user=login, passwd=passw) cursor = conn.cursor() -except MySQLdb.Error, e: - print "Error %d: %s" % (e.args[0], e.args[1]) +except MySQLdb.Error as e: + print("Error %d: %s" % (e.args[0], e.args[1])) sys.exit(1) values = {} if len(sys.argv) == 2 and sys.argv[1] == "autoconf": - print "yes" + print("yes") elif len(sys.argv) == 2 and sys.argv[1] == "config": - print "graph_title %s" % label - print "graph_vlabel %s" % vlabel - print "graph_category percona" - print "" + print("graph_title %s" % label) + print("graph_vlabel %s" % vlabel) + print("graph_category db") + print() try: cursor.execute(query) results = cursor.fetchall() for result in results: - print "%s_size.label %s" % (result[0], result[0]) + print("%s_size.label %s" % (result[0], result[0])) - except MySQLdb.Error, e: - print "Error %d: %s" % (e.args[0], e.args[1]) + except MySQLdb.Error as e: + print("Error %d: %s" % (e.args[0], e.args[1])) else: try: @@ -147,10 +153,10 @@ else: results = cursor.fetchall() for result in results: - print "%s_size.value %s" % (result[0], result[1]) + print("%s_size.value %s" % (result[0], result[1])) - except MySQLdb.Error, e: - print "Error %d: %s" % (e.args[0], e.args[1]) + except MySQLdb.Error as e: + print("Error %d: %s" % (e.args[0], e.args[1])) if conn: conn.close() diff --git a/plugins/network/pf b/plugins/pf/pf similarity index 100% rename from plugins/network/pf rename to plugins/pf/pf diff --git a/plugins/network/pf_bytes b/plugins/pf/pf_bytes similarity index 100% rename from plugins/network/pf_bytes rename to plugins/pf/pf_bytes diff --git a/plugins/network/pf_ipv4_ipv6_packets b/plugins/pf/pf_ipv4_ipv6_packets similarity index 100% rename from plugins/network/pf_ipv4_ipv6_packets rename to plugins/pf/pf_ipv4_ipv6_packets diff --git a/plugins/network/pf_openbsd b/plugins/pf/pf_openbsd similarity index 100% rename from plugins/network/pf_openbsd rename to plugins/pf/pf_openbsd diff --git a/plugins/network/pf_packets b/plugins/pf/pf_packets similarity index 100% rename from plugins/network/pf_packets rename to plugins/pf/pf_packets diff --git a/plugins/network/pf_states b/plugins/pf/pf_states similarity index 100% rename from plugins/network/pf_states rename to plugins/pf/pf_states diff --git a/plugins/network/pf_tables_ b/plugins/pf/pf_tables_ old mode 100644 new mode 100755 similarity index 94% rename from plugins/network/pf_tables_ rename to plugins/pf/pf_tables_ index 3e98903f..b0caf141 --- a/plugins/network/pf_tables_ +++ b/plugins/pf/pf_tables_ @@ -91,7 +91,7 @@ if ( defined($ARGV[0])) { print "graph_args --base 1000 -l 0\n"; print "graph_vlabel Users\n"; print "graph_scale no\n"; - print "graph_category pf\n"; + print "graph_category network\n"; print "graph_printf %3.0lf\n"; print "users.label users\n"; @@ -108,24 +108,23 @@ if ( defined($ARGV[0])) { print "graph_args --base 1024 -l 0\n"; print "graph_vlabel Bandwidth\n"; print "graph_scale yes\n"; - print "graph_category pf\n"; + print "graph_category network\n"; # print "graph_printf %3.0lf\n"; print "in.label in\n"; print "in.type DERIVE\n"; - print "in.draw AREASTACK\n"; + print "in.draw AREA\n"; print "in.colour C00000\n"; print "in.cdef in,8,*\n"; print "in.min 0\n"; print "in.graph no\n"; - print "out.label out\n"; + print "out.label bps\n"; print "out.type DERIVE\n"; print "out.negative in\n"; - print "out.draw AREASTACK\n"; - print "out.colour 0000C0\n"; + print "out.draw AREA\n"; + print "out.colour COLOUR18\n"; print "out.cdef out,8,*\n"; print "out.min 0\n"; - print "out.graph no\n"; foreach my $field (qw(in out)) { print_thresholds($field); @@ -191,9 +190,10 @@ sub tables { foreach (split(/\n/, $output)) { - if (m|^[cpairhC\-]{7}\s+(\w+)$|) { + if (m|^[cpairhC\-]{7}\s+(\S+)$|) { $name = $1; - $tables{$name}->{"name"} = $1; + $name =~ s/\-/_/; + $tables{$name}->{"name"} = $name; next; } diff --git a/plugins/apache/eaccelerator b/plugins/php/eaccelerator similarity index 99% rename from plugins/apache/eaccelerator rename to plugins/php/eaccelerator index ef0871e3..9a9fcbc7 100755 --- a/plugins/apache/eaccelerator +++ b/plugins/php/eaccelerator @@ -77,7 +77,7 @@ if [ "$1" = "config" ]; then echo 'graph_title Eaccelerator usage ' echo 'graph_args -l 0' - echo 'graph_category apache' + echo 'graph_category webserver' echo 'graph_info This graph shows performance of the eaccelerator module on WWW server.' echo 'memorysize.label total' diff --git a/plugins/php/eaccelerator-python b/plugins/php/eaccelerator-python index 2260db1f..dc2bad21 100755 --- a/plugins/php/eaccelerator-python +++ b/plugins/php/eaccelerator-python @@ -32,7 +32,7 @@ config = { 'graph_title eacceleratory memory usage\n' + 'graph_info This graph shows memory performance of PHP eaccelerator module\n' + 'graphs_args -1 0\n' + - 'graph_category php-eaccelerator\n' + + 'graph_category webserver\n' + 'memorysize.label total\n' + 'memorysize.draw AREA\n' + @@ -47,7 +47,7 @@ config = { 'graph_title eacceleratory cached scripts\n' + 'graph_info This graph shows how many scripts are cached by PHP eaccelerator module\n' + 'graphs_args -1 0\n' + - 'graph_category php-eaccelerator\n' + + 'graph_category webserver\n' + 'cachedscripts.label cached scripts\n' + 'cachedscripts.draw LINE1\n' + diff --git a/plugins/php/eaccelerator-usage b/plugins/php/eaccelerator-usage index 6f72e1b4..f95a19d1 100755 --- a/plugins/php/eaccelerator-usage +++ b/plugins/php/eaccelerator-usage @@ -51,7 +51,7 @@ if ($#ARGV eq 0) { if ($ARGV[0] eq 'config') { print "eAccelerator\n"; print "graph_title eAccelerator\n"; - print "graph_category php-eaccelerator\n"; + print "graph_category webserver\n"; print "memusage.label Memory Usage %\n"; print "memusage.warning 95\n"; print "memusage.critical 95\n"; diff --git a/plugins/hhvm/hhvm_ b/plugins/php/hhvm_ similarity index 98% rename from plugins/hhvm/hhvm_ rename to plugins/php/hhvm_ index e3db61be..34b23e59 100755 --- a/plugins/hhvm/hhvm_ +++ b/plugins/php/hhvm_ @@ -89,7 +89,7 @@ if ( exists $ARGV[0] && 'config' eq $ARGV[0] ) { multigraph hhvm_${graphName}_threads graph_title HHVM Threads ${name} graph_args --base 1000 -graph_category hhvm +graph_category webserver threads.label Threads load.label Active Workers queued.label Queued Jobs @@ -97,7 +97,7 @@ queued.label Queued Jobs multigraph hhvm_${graphName}_sizes graph_title HHVM Sizes ${name} graph_args --base 1024 -graph_category hhvm +graph_category webserver hhbc-roarena-capac.label HHBC Arena Capacity tc-hotsize.label TC Hot Size tc-hotsize.info Translation Cache Hot Size diff --git a/plugins/php/php-cgi b/plugins/php/php-cgi index 7e809a78..c9b8009e 100755 --- a/plugins/php/php-cgi +++ b/plugins/php/php-cgi @@ -23,7 +23,7 @@ fi if [ "$1" = "config" ]; then echo "graph_title PHP CGI Memory"; echo "graph_vlabel PHP CGI Memory usage in GB"; - echo "graph_category php-cgi"; + echo "graph_category processes"; echo "graph_args -l 0"; echo "php_cgi_ram.label PHP CGI Used RAM"; echo "php_cgi_ram.draw LINE2"; diff --git a/plugins/php/php-fastcgi b/plugins/php/php-fastcgi index b79d3168..15cb1ec3 100755 --- a/plugins/php/php-fastcgi +++ b/plugins/php/php-fastcgi @@ -90,7 +90,7 @@ if (exists $ARGV[0] and $ARGV[0] eq "config") { print "graph_title PHP CGI [MB]\n"; print "graph_vlabel PHP CGI Memory usage\n"; - print "graph_category php-fastcgi\n"; + print "graph_category processes\n"; print "graph_args -l 0\n"; print "php_cgi_ram.label PHP CGI Used RAM\n"; print "php_cgi_ram.draw LINE2\n"; diff --git a/plugins/php/php5-fpm_status b/plugins/php/php5-fpm_status index b05bb485..73cc92c4 100755 --- a/plugins/php/php5-fpm_status +++ b/plugins/php/php5-fpm_status @@ -24,7 +24,7 @@ if [ "$1" = "config" ]; then echo "graph_args --base 1000 -l 0" echo "graph_vlabel Processes" echo "graph_scale yes" - echo "graph_category php-fpm" + echo "graph_category processes" echo "graph_info This graph shows the php5-fpm process manager status from pool: $pool" echo "active.label Active processes" echo "active.type GAUGE" diff --git a/plugins/php/php_apc_ b/plugins/php/php_apc_ index 274c8d2b..f0824171 100755 --- a/plugins/php/php_apc_ +++ b/plugins/php/php_apc_ @@ -56,7 +56,7 @@ cat <<'EOM' graph_title APC Memory usage graph_args -l 0 --base 1024 graph_vlabel Memory usage -graph_category php-apc +graph_category memory graph_order mem_used mem_avail graph_total Total mem_avail.label Memory available @@ -76,7 +76,7 @@ cat <<'EOM' graph_title APC Hits graph_args -l 0 graph_vlabel Cache hits -graph_category php-apc +graph_category memory graph_total Total num_misses.label Misses num_misses.draw AREA @@ -94,8 +94,9 @@ if [ "$1" = "config" ] && [ "$act" = "percents" ]; then cat <<'EOM' graph_title APC Percents graph_args -l 0 --upper-limit 100 +graph_scale no graph_vlabel Cache Percents % -graph_category php-apc +graph_category memory hits.label Hits hits.draw AREA hits.min 0 diff --git a/plugins/php/php_eaccelerator b/plugins/php/php_eaccelerator index 0091d7b0..d8a8a818 100755 --- a/plugins/php/php_eaccelerator +++ b/plugins/php/php_eaccelerator @@ -34,7 +34,7 @@ url = ENV['url'] || 'http://127.0.0.1/control.php' if ARGV[0]=="config" print "EAccelerator Monitoring\n" print "graph_title PHP Eaccelerator\n" - print "graph_category php-eaccelerator\n" + print "graph_category webserver\n" print "Memoryusagepercentage.label Memory Usage %\n" print "Memoryusagepercentage.warning 95\n" print "Memoryusagepercentage.critical 95\n" diff --git a/plugins/php/php_errors_ b/plugins/php/php_errors_ old mode 100644 new mode 100755 index e8788368..dd073a58 --- a/plugins/php/php_errors_ +++ b/plugins/php/php_errors_ @@ -47,6 +47,7 @@ fi if [[ $1 == config ]]; then echo 'graph_title PHP Errors from ' $LOGS echo 'graph_args --base 1000 -l 0' + echo 'graph_category webserver' echo 'graph_vlabel Errors' echo 'LogWarning.label PHP Warning errors' echo 'LogNotice.label PHP Notice errors' diff --git a/plugins/php/php_fpm_process b/plugins/php/php_fpm_process index 89b6acfd..873b814e 100755 --- a/plugins/php/php_fpm_process +++ b/plugins/php/php_fpm_process @@ -126,7 +126,7 @@ graph_title php5-fpm processes for $pool graph_args --base 1000 -l 0 graph_vlabel Processes graph_scale yes -graph_category php-fpm +graph_category processes graph_info This graph shows the php5-fpm process manager status from pool: $pool active.label Active processes active.type GAUGE @@ -146,7 +146,7 @@ graph_title php5-fpm slow requests $pool graph_args --base 1000 -l 0 graph_vlabel Slow requests graph_scale yes -graph_category php-fpm +graph_category processes graph_info This graph shows the php5-fpm slow request from pool: $pool slow_requests.label Slow requests slow_requests.type DERIVE diff --git a/plugins/php/php_opcache b/plugins/php/php_opcache index 04af966e..4055d72f 100755 --- a/plugins/php/php_opcache +++ b/plugins/php/php_opcache @@ -36,7 +36,7 @@ cat <<'EOM' graph_title OPCache Memory usage graph_args -l 0 --base 1024 graph_vlabel Memory usage -graph_category php-opcache +graph_category memory graph_order mem_used mem_free mem_wasted graph_total Total mem_free.label Memory Free diff --git a/plugins/php/php_opcache.php b/plugins/php/php_opcache.php old mode 100644 new mode 100755 index c23d62b0..a4437a35 --- a/plugins/php/php_opcache.php +++ b/plugins/php/php_opcache.php @@ -5,7 +5,7 @@ if (function_exists('opcache_get_status')) { - $data = opcache_get_status(); + $data = opcache_get_status(false); $output = array( 'mem_used.value' => $data['memory_usage']['used_memory'], 'mem_free.value' => $data['memory_usage']['free_memory'], diff --git a/plugins/php/php_sessions b/plugins/php/php_sessions index 50b1629c..eb947f42 100755 --- a/plugins/php/php_sessions +++ b/plugins/php/php_sessions @@ -53,7 +53,7 @@ fi if [ "$1" = "config" ]; then echo 'graph_title Apache/PHP Sessions' echo 'graph_info This graph shows active Apache/PHP sessions.' - echo 'graph_category php-sessions' + echo 'graph_category webserver' echo "graph_args --lower-limit 0" echo 'graph_vlabel n' diff --git a/plugins/php/php_time_execution b/plugins/php/php_time_execution old mode 100644 new mode 100755 index 9f0c252f..bae1252f --- a/plugins/php/php_time_execution +++ b/plugins/php/php_time_execution @@ -53,7 +53,7 @@ if [ "$1" = "config" ]; then echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel Time in microsecond' - echo "graph_category php-time" + echo "graph_category webserver" echo "graph_info This graph shows load time in ms of $target" echo "minloadtime.label Min time" echo "minloadtime.info Min time" diff --git a/plugins/network/ping/ping-day.png b/plugins/ping/example-graphs/ping-day.png similarity index 100% rename from plugins/network/ping/ping-day.png rename to plugins/ping/example-graphs/ping-day.png diff --git a/plugins/network/fping_ b/plugins/ping/fping_ similarity index 100% rename from plugins/network/fping_ rename to plugins/ping/fping_ diff --git a/plugins/network/multi_tcp_ping b/plugins/ping/multi_tcp_ping similarity index 100% rename from plugins/network/multi_tcp_ping rename to plugins/ping/multi_tcp_ping diff --git a/plugins/network/multiping b/plugins/ping/multiping similarity index 100% rename from plugins/network/multiping rename to plugins/ping/multiping diff --git a/plugins/network/ping/ping b/plugins/ping/ping similarity index 100% rename from plugins/network/ping/ping rename to plugins/ping/ping diff --git a/plugins/network/ping-with-ceil b/plugins/ping/ping-with-ceil similarity index 100% rename from plugins/network/ping-with-ceil rename to plugins/ping/ping-with-ceil diff --git a/plugins/network/ping_host b/plugins/ping/ping_host similarity index 100% rename from plugins/network/ping_host rename to plugins/ping/ping_host diff --git a/plugins/network/pinger b/plugins/ping/pinger similarity index 100% rename from plugins/network/pinger rename to plugins/ping/pinger diff --git a/plugins/snmp/snmp__poseidon-sensors b/plugins/poseidon/snmp__poseidon-sensors similarity index 100% rename from plugins/snmp/snmp__poseidon-sensors rename to plugins/poseidon/snmp__poseidon-sensors diff --git a/plugins/mail/greyfix b/plugins/postfix/greyfix similarity index 99% rename from plugins/mail/greyfix rename to plugins/postfix/greyfix index ee4da296..87d0d4fd 100755 --- a/plugins/mail/greyfix +++ b/plugins/postfix/greyfix @@ -130,7 +130,7 @@ def print_config(): print 'graph_title Greyfix triplets by age' print 'graph_vlabel Number of triplets' print 'graph_info The amount of triplets in the greyfix database with a certain age' - print 'graph_category postfix' + print 'graph_category mail' print 'graph_total All triplets' print 'graph_args --lower-limit 0 --base 1000' diff --git a/plugins/mail/policyd-spf-python b/plugins/postfix/policyd-spf-python similarity index 94% rename from plugins/mail/policyd-spf-python rename to plugins/postfix/policyd-spf-python index f51a7b8b..93c53b5e 100755 --- a/plugins/mail/policyd-spf-python +++ b/plugins/postfix/policyd-spf-python @@ -25,7 +25,7 @@ # Configuration # -STAT_FILE=${STAT_FILE:-/var/lib/munin/plugin-state/plugin-plcyd-spf-python.state} +STAT_FILE=${STAT_FILE:-$MUNIN_PLUGSTATE/plugin-plcyd-spf-python.state} LOGFILE=${logfile:-/var/log/mail.log} if [ "$1" = "autoconf" ]; then @@ -35,7 +35,7 @@ fi if [ "$1" = "config" ]; then echo 'graph_title SPF Check Results' - echo 'graph_category postfix' + echo 'graph_category mail' echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel Count/s' diff --git a/plugins/mail/postfix-policyd b/plugins/postfix/postfix-policyd similarity index 100% rename from plugins/mail/postfix-policyd rename to plugins/postfix/postfix-policyd diff --git a/plugins/mail/postfix-queue-size b/plugins/postfix/postfix-queue-size similarity index 99% rename from plugins/mail/postfix-queue-size rename to plugins/postfix/postfix-queue-size index d4d5f81b..f471b260 100755 --- a/plugins/mail/postfix-queue-size +++ b/plugins/postfix/postfix-queue-size @@ -103,7 +103,7 @@ case $1 in cat <<'EOF' graph_title Postfix Queue Size graph_vlabel KB in Queue -graph_category postfix +graph_category mail graph_total Total active.label active deferred.label deferred diff --git a/plugins/mail/postfix-rbl-blocked-mails b/plugins/postfix/postfix-rbl-blocked-mails similarity index 100% rename from plugins/mail/postfix-rbl-blocked-mails rename to plugins/postfix/postfix-rbl-blocked-mails diff --git a/plugins/mail/postfix_filtered b/plugins/postfix/postfix_filtered similarity index 98% rename from plugins/mail/postfix_filtered rename to plugins/postfix/postfix_filtered index 1ae69fef..8cb70370 100755 --- a/plugins/mail/postfix_filtered +++ b/plugins/postfix/postfix_filtered @@ -39,7 +39,7 @@ fi LOGDIR=${logdir:-/var/log/mail} MAIL_LOG=$LOGDIR/${logfile:-info} LOGTAIL=${logtail:-`which logtail`} -STATEFILE=/var/lib/munin/plugin-state/postfix_mailfiltered.offset +STATEFILE=$MUNIN_PLUGSTATE/postfix_mailfiltered.offset if [ "$1" = "autoconf" ]; then if [ -f "${MAIL_LOG}" -a -n "${LOGTAIL}" -a -x "${LOGTAIL}" ] ; then diff --git a/plugins/mail/postfix_filtered_awk b/plugins/postfix/postfix_filtered_awk similarity index 97% rename from plugins/mail/postfix_filtered_awk rename to plugins/postfix/postfix_filtered_awk index c83c1c36..fde5dbc0 100755 --- a/plugins/mail/postfix_filtered_awk +++ b/plugins/postfix/postfix_filtered_awk @@ -35,7 +35,7 @@ export POLICY LOGDIR=${logdir:-/var/log/mail} MAIL_LOG=$LOGDIR/${logfile:-info} LOGTAIL=${logtail:-`which logtail`} -STATEFILE=/var/lib/munin/plugin-state/postfix_mailfiltered_test.offset +STATEFILE=$MUNIN_PLUGSTATE/postfix_mailfiltered_test.offset if [ "$1" = "autoconf" ]; then if [ -f "${MAIL_LOG}" -a -n "${LOGTAIL}" -a -x "${LOGTAIL}" ] ; then diff --git a/plugins/postfix/postfix_mail_stats b/plugins/postfix/postfix_mail_stats index 2ac2866e..b300b3a8 100755 --- a/plugins/postfix/postfix_mail_stats +++ b/plugins/postfix/postfix_mail_stats @@ -36,10 +36,10 @@ fi if [ "$1" = "config" ]; then echo 'graph_title Postfix Mail Counter' - echo 'graph_category postfix' echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel Hourly Messages' - echo 'recieved.label Delivered' + echo 'graph_category mail' + echo 'received.label Delivered' echo 'sent.label Outgoing' echo 'rejecthelo.label Invalid HELO' echo 'rejectsenderdomain.label need FQDN' @@ -49,21 +49,21 @@ if [ "$1" = "config" ]; then exit 0 fi -echo -en "recieved.value " -echo $(grep "postfix/lmtp\[" $LOGFILE | grep "$DATE" -c) +echo -en "received.value " +echo $(grep "status=sent (delivered" $LOGFILE | grep "$DATE" | wc -l) echo -n echo -en "sent.value " -echo $(grep "postfix/smtp\[" $LOGFILE | grep "$DATE" -c) +echo $(grep "status=sent (250" $LOGFILE | grep "$DATE" | wc -l) echo -en "rejecthelo.value " -echo $(grep "Helo command rejected: need fully-qualified hostname" $LOGFILE | grep "$DATE" -c) +echo $(grep "Helo command rejected: need fully-qualified hostname" $LOGFILE | grep "$DATE" | wc -l) echo -en "rejectsenderdomain.value " -echo $(grep "Sender address rejected: Domain not found" $LOGFILE | grep "$DATE" -c) +echo $(grep "Sender address rejected: Domain not found" $LOGFILE | grep "$DATE" | wc -l) echo -en "denyrelay.value " -echo $(grep "Relay access denied" $LOGFILE | grep "$DATE" -c) +echo $(grep "Relay access denied" $LOGFILE | grep "$DATE" | wc -l) echo -en "spamhaus.value " -echo $(grep "blocked using sbl-xbl.spamhaus.org" $LOGFILE | grep "$DATE" -c) +echo $(grep "blocked using sbl-xbl.spamhaus.org" $LOGFILE | grep "$DATE" | wc -l) echo -en "spamcop.value " -echo $(grep "blocked using bl.spamcop.net" $LOGFILE | grep "$DATE" -c) +echo $(grep "blocked using bl.spamcop.net" $LOGFILE | grep "$DATE" | wc -l) diff --git a/plugins/mail/postfix_mail_stats b/plugins/postfix/postfix_mail_stats1 similarity index 82% rename from plugins/mail/postfix_mail_stats rename to plugins/postfix/postfix_mail_stats1 index c6a34db3..b2132a99 100755 --- a/plugins/mail/postfix_mail_stats +++ b/plugins/postfix/postfix_mail_stats1 @@ -36,9 +36,10 @@ fi if [ "$1" = "config" ]; then echo 'graph_title Postfix Mail Counter' + echo 'graph_category mail' echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel Hourly Messages' - echo 'received.label Delivered' + echo 'recieved.label Delivered' echo 'sent.label Outgoing' echo 'rejecthelo.label Invalid HELO' echo 'rejectsenderdomain.label need FQDN' @@ -48,21 +49,21 @@ if [ "$1" = "config" ]; then exit 0 fi -echo -en "received.value " -echo $(grep "status=sent (delivered" $LOGFILE | grep "$DATE" | wc -l) +echo -en "recieved.value " +echo $(grep "postfix/lmtp\[" $LOGFILE | grep "$DATE" -c) echo -n echo -en "sent.value " -echo $(grep "status=sent (250" $LOGFILE | grep "$DATE" | wc -l) +echo $(grep "postfix/smtp\[" $LOGFILE | grep "$DATE" -c) echo -en "rejecthelo.value " -echo $(grep "Helo command rejected: need fully-qualified hostname" $LOGFILE | grep "$DATE" | wc -l) +echo $(grep "Helo command rejected: need fully-qualified hostname" $LOGFILE | grep "$DATE" -c) echo -en "rejectsenderdomain.value " -echo $(grep "Sender address rejected: Domain not found" $LOGFILE | grep "$DATE" | wc -l) +echo $(grep "Sender address rejected: Domain not found" $LOGFILE | grep "$DATE" -c) echo -en "denyrelay.value " -echo $(grep "Relay access denied" $LOGFILE | grep "$DATE" | wc -l) +echo $(grep "Relay access denied" $LOGFILE | grep "$DATE" -c) echo -en "spamhaus.value " -echo $(grep "blocked using sbl-xbl.spamhaus.org" $LOGFILE | grep "$DATE" | wc -l) +echo $(grep "blocked using sbl-xbl.spamhaus.org" $LOGFILE | grep "$DATE" -c) echo -en "spamcop.value " -echo $(grep "blocked using bl.spamcop.net" $LOGFILE | grep "$DATE" | wc -l) +echo $(grep "blocked using bl.spamcop.net" $LOGFILE | grep "$DATE" -c) diff --git a/plugins/mail/postfix_mailfiltered b/plugins/postfix/postfix_mailfiltered similarity index 96% rename from plugins/mail/postfix_mailfiltered rename to plugins/postfix/postfix_mailfiltered index d11712b7..f96687df 100755 --- a/plugins/mail/postfix_mailfiltered +++ b/plugins/postfix/postfix_mailfiltered @@ -15,7 +15,7 @@ mktemp -t MAIL_LOG=${logfile:-/var/log/mail.log} LOGTAIL=${logtail:-`which logtail`} -STATEFILE=/var/lib/munin/plugin-state/postfix_mailfiltered.offset +STATEFILE=$MUNIN_PLUGSTATE/postfix_mailfiltered.offset if [ "$1" = "autoconf" ]; then if [ -f "${MAIL_LOG}" -a -n "${LOGTAIL}" -a -x "${LOGTAIL}" ] ; then diff --git a/plugins/postfix/postfix_mailqueue_ b/plugins/postfix/postfix_mailqueue_ index 8154a677..c2d4cb7b 100755 --- a/plugins/postfix/postfix_mailqueue_ +++ b/plugins/postfix/postfix_mailqueue_ @@ -106,7 +106,7 @@ case $1 in echo "graph_title Postfix Mailqueue $CONFIG"; cat <<'EOF' graph_vlabel Mails in queue -graph_category postfix +graph_category mail graph_total Total active.label active deferred.label deferred diff --git a/plugins/postfix/postfix_mailqueuelog_ b/plugins/postfix/postfix_mailqueuelog_ index 4a7cf79e..4ccc8f6a 100755 --- a/plugins/postfix/postfix_mailqueuelog_ +++ b/plugins/postfix/postfix_mailqueuelog_ @@ -144,7 +144,7 @@ if ($ARGV[0] and $ARGV[0] eq "config") print "graph_vlabel Mails in Queue log\n"; print "graph_scale no\n"; # so we do not print "micro, milli, kilo, etc" # print "graph_total Total\n"; - print "graph_category postfix\n"; + print "graph_category mail\n"; foreach my $i (@status_list) { if ($descriptions{$i}) diff --git a/plugins/mail/postfix_mailstats b/plugins/postfix/postfix_mailstats similarity index 98% rename from plugins/mail/postfix_mailstats rename to plugins/postfix/postfix_mailstats index 0f9b2203..c21e2df2 100755 --- a/plugins/mail/postfix_mailstats +++ b/plugins/postfix/postfix_mailstats @@ -72,7 +72,7 @@ sub config print "graph_vlabel mails / \${graph_period}\n"; print "graph_scale no\n"; print "graph_total Total\n"; - print "graph_category postfix\n"; + print "graph_category mail\n"; print "delivered.label delivered\n"; print "delivered.type ABSOLUTE\n"; print "delivered.draw AREA\n"; diff --git a/plugins/postfix/postfix_mailstats_ b/plugins/postfix/postfix_mailstats_ index 59d7f4ba..b3a7f489 100755 --- a/plugins/postfix/postfix_mailstats_ +++ b/plugins/postfix/postfix_mailstats_ @@ -145,7 +145,7 @@ if ( $ARGV[0] and $ARGV[0] eq "config" ) print "graph_vlabel mails / \${graph_period}\n"; print "graph_scale no\n"; print "graph_total Total\n"; - print "graph_category postfix\n"; + print "graph_category mail\n"; print "delivered.label delivered\n"; print "delivered.type DERIVE\n"; print "delivered.draw AREA\n"; diff --git a/plugins/postfix/postfix_mailvolume_multi b/plugins/postfix/postfix_mailvolume_multi index c155e31c..4e7401e5 100755 --- a/plugins/postfix/postfix_mailvolume_multi +++ b/plugins/postfix/postfix_mailvolume_multi @@ -158,7 +158,7 @@ if ($ARGV[0] and $ARGV[0] eq "config") print "graph_args --base 1000 -l 0\n"; print "graph_vlabel bytes / \${graph_period}\n"; print "graph_scale yes\n"; - print "graph_category postfix\n"; + print "graph_category mail\n"; print "graph_total Throughput sum\n"; # loop through the postfix names and create per config an entry foreach $syslog_name (@postfix_syslog_name) diff --git a/plugins/postfix/postfix_stats b/plugins/postfix/postfix_stats new file mode 100755 index 00000000..89ac0fdb --- /dev/null +++ b/plugins/postfix/postfix_stats @@ -0,0 +1,158 @@ +#!/bin/sh +# -*- sh -*- + +: <<=cut + +=head1 NAME + +postfix_stats - Munin plugin to monitor postfix statistics. + +=head1 APPLICABLE SYSTEMS + +Any system where pflogsumm script can be executed. + +=head1 CONFIGURATION + +There is no default configuration. This is an example config for Ubuntu: + + [postfix_stats*] + group adm + env.logfiles /var/log/mail.log /var/log/mail.log.1 + env.pflogsumm pflogsumm + +env.logfiles contains space separated syslog logfiles, usually current log +and previous log. You can add more log files if you want, but this may +increase the time required for analysis. + +env.pflogsumm The "pflogsumm" script, can be pflogsumm.pl if it was manually +downloaded and installed, or "pflogsumm" if it was installed by a package +manager (like apt-get). + +Use the last part of the symlink name for filtering by hostname from logfile, +which contains logs from multiple hosts (e.g. received via rsyslog from remote +hosts). + +For example: + + cd /etc/munin/plugins + ln -s /path/to/postfix_stats postfix_stats_HOSTNAME + +=head1 INTERPRETATION + +This plugin displays the messages: received, delivered, rejected... +Average per minute is made and it shows the total message count. +It is useful to know the load and messages being processed. + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=head1 VERSION + 0.3 support for hostname grep (useful when multiple hosts in one log) + 0.2 plugin completely rewritten + 0.1 first release. + +=head1 BUGS + +None known + +=head1 AUTHOR + +Originally: David Obando (david@cryptix.de) +Modified by: github.com/cristiandeluxe +Modified by: github.com/4nd3r +Thanks to: sumpfralle + +=head1 LICENSE + +GPLv2 + +=cut + +#set -xv +MAIL_LOG="${logfiles:-/var/log/mail.log /var/log/mail.log.1}" + +FILTER_HOSTNAME=$(basename "$0" | sed -r 's/postfix_stats_?//') + +# shellcheck disable=SC2154 +PFLOGSUMM="${pflogsum}" +[ -z "$PFLOGSUMM" ] && PFLOGSUMM="$(which pflogsumm pflogsumm.pl | head -1)" + +# Fields (Array to avoid code duplication) must be space separated +FIELDS_ARR="received delivered forwarded deferred bounced rejected held discarded" + +# +# Autoconf Section +# +if [ "$1" = 'autoconf' ]; then + # Check if pflogsumm exist + if [ -z "${PFLOGSUMM}" ] + then + echo 'no (pflogsum not found in your system)' + else + echo 'yes' + fi + exit 0 +fi + +# +# Config Section +# +if [ "$1" = 'config' ]; then + if [ -n "${FILTER_HOSTNAME}" ]; then + echo "graph_title Postfix statistics for ${FILTER_HOSTNAME}" + else + echo 'graph_title Postfix statistics' + fi + echo 'graph_vlabel Postfix statistics' + echo 'graph_category mail' + echo 'graph_scale no' + echo 'graph_period minute' + echo 'graph_total Total' + + # Generate config for each field + for i in $FIELDS_ARR; do + echo "${i}.label ${i}" + echo "${i}.type DERIVE" + echo "${i}.min 0" + echo "${i}.draw AREASTACK" + done + + exit 0 +fi + +# +# Plugin Script +# + +# Variable to store the pflogsumm result. +if [ -n "${FILTER_HOSTNAME}" ]; then + # shellcheck disable=SC2086 + TMP_RAW=$(grep -Fh " ${FILTER_HOSTNAME} postfix/" ${MAIL_LOG} | "${PFLOGSUMM}" -d today --detail 0 --zero-fill) +else + # shellcheck disable=SC2086 + TMP_RAW=$("${PFLOGSUMM}" -d today --detail 0 --zero-fill ${MAIL_LOG}) +fi + +# Parse value from Raw result +# +# Return digit if regex are parsed correctly +# +# Return U (undefined) if any error occurs +# +parseValue() { + # shellcheck disable=SC2155 + local TMP_RETURN=$(echo "${TMP_RAW}" | grep -Ei "^[[:space:]]+[[:digit:]]+[[:space:]]+${1}.*$" | grep -oEi '[[:digit:]]+[[:space:]]+' | head -n 1 | sed 's: ::g') + if [ -z "${TMP_RETURN}" ]; then + echo 'U' + else + echo "${TMP_RETURN}" + fi +} + +# Print results +for i in $FIELDS_ARR; do + printf "%s.value " "${i}" + parseValue "${i}" +done diff --git a/plugins/mail/postfwd2 b/plugins/postfix/postfwd2 similarity index 99% rename from plugins/mail/postfwd2 rename to plugins/postfix/postfwd2 index f99a8ae6..edae34a8 100755 --- a/plugins/mail/postfwd2 +++ b/plugins/postfix/postfwd2 @@ -199,7 +199,7 @@ if( (defined $ARGV[0]) && ($ARGV[0] eq 'config') ) { print 'graph_vlabel ', $graphs{$key}->{vlabel}, "\n"; my $args = ($key eq 'cache_stats') ? ' --upper-limit 100 --rigid' : ''; print 'graph_args --lower-limit 0', $args, "\n"; - print 'graph_category mail', "\n"; + print 'graph_category fw', "\n"; if ($key eq 'policy_matchs') { print 'graph_width 600', "\n"; my @pol_keys = sort { $graphs{$key}->{series}->{$b}->{value} <=> $graphs{$key}->{series}->{$a}->{value} } keys %{$graphs{$key}->{series}}; @@ -214,7 +214,7 @@ if( (defined $ARGV[0]) && ($ARGV[0] eq 'config') ) { print 'graph_vlabel ', $graphs{$key}->{vlabel}, "\n"; print 'graph_width 600', "\n"; print 'graph_args --lower-limit 0 ', $args, "\n"; - print 'graph_category other', "\n"; + print 'graph_category fw', "\n"; print $label, '.label ', $graphs{$key}->{series}->{$label}->{label}, "\n"; print $label, '.draw LINE', "\n"; print $label, '.type GAUGE', "\n"; diff --git a/plugins/mail/postgrey b/plugins/postfix/postgrey similarity index 96% rename from plugins/mail/postgrey rename to plugins/postfix/postgrey index 1c9f1eb9..e5c9ebc6 100755 --- a/plugins/mail/postgrey +++ b/plugins/postfix/postgrey @@ -14,7 +14,7 @@ mktemp -t } MAIL_LOG=${logfile:-/var/log/mail.log} -STATEFILE=/var/lib/munin/plugin-state/postgrey.offset +STATEFILE=$MUNIN_PLUGSTATE/postgrey.offset LOGTAIL=${logtail:-`which logtail`} if [ "$1" = "autoconf" ]; then diff --git a/plugins/mail/postgrey-new b/plugins/postfix/postgrey-new similarity index 98% rename from plugins/mail/postgrey-new rename to plugins/postfix/postgrey-new index 73912086..3a8944a1 100755 --- a/plugins/mail/postgrey-new +++ b/plugins/postfix/postgrey-new @@ -29,7 +29,7 @@ use strict; use warnings; my $maillog= $ENV{'logfile'} || "/var/log/mail.log"; -my $statefile= $ENV{'statefile'} || "/var/lib/munin/plugin-state/postgrey-new.state"; +my $statefile= $ENV{'statefile'} || "$ENV{MUNIN_PLUGSTATE}/postgrey-new.state"; my $greylisted=0; my $greylisted_old=0; diff --git a/plugins/postgresql/pg__connections b/plugins/postgresql/pg__connections index 8884466f..f6ca4018 100755 --- a/plugins/postgresql/pg__connections +++ b/plugins/postgresql/pg__connections @@ -66,7 +66,7 @@ if (exists $ARGV[0]) { print "graph_title PostgresSQL active connections\n"; print "graph_args -l 0 --base 1000\n"; print "graph_vlabel Connections\n"; - print "graph_category Postgresql\n"; + print "graph_category db\n"; print "graph_info Shows active Postgresql connections from $dbname\n"; diff --git a/plugins/postgresql/pgbouncer_ b/plugins/postgresql/pgbouncer_ index 91aeef66..7e429671 100755 --- a/plugins/postgresql/pgbouncer_ +++ b/plugins/postgresql/pgbouncer_ @@ -16,11 +16,17 @@ my $db_user = $ENV{'pgbouncer_user'} || 'postgres'; my $db_port = $ENV{'pgbouncer_port'} || '6432'; my $db_host = $ENV{'pgbouncer_host'} || 'localhost'; my $db_pass = $ENV{'pgbouncer_pass'} || ''; +my $db_pool = $ENV{'pgbouncer_pool'} || ''; my $db_name = 'pgbouncer'; my @data = (); # get the DB (pool) name we want to fetch $plugin_name =~ /pgbouncer_(.*)$/; -my $pool_name = $1; +my $plugin_suffix = $1; +#if pool name is specified explicitly in config file +#use plugin name together with pool name in graph title: +my $pool_name = ($db_pool) ? $db_pool : $plugin_suffix; +my $plugin_title = ($db_pool) ? $plugin_suffix." ".$pool_name : $pool_name; + # bail if no name if (!$pool_name) { @@ -52,32 +58,33 @@ if (defined($ARGV[0])) { # create the basic RRD # stats: average connections + print "multigraph ".$plugin_name."_stats_avg_req\n"; - print "graph_title PgBouncer $pool_name average connections\n"; + print "graph_title PgBouncer $plugin_title average connections\n"; print "graph_args --base 1000\n"; # numbers not bytes print "graph_vlabel Average connections\n"; print "graph_scale no\n"; # so we do not print "micro, milli, kilo, etc" - print "graph_category pgbouncer\n"; + print "graph_category db\n"; print $pool_name."_avg_req.type GAUGE\n"; print $pool_name."_avg_req.label Avg Req\n"; print $pool_name."_avg_req.min 0\n"; print $pool_name."_avg_req.draw LINE2\n"; # stats: average time for query print "multigraph ".$plugin_name."_stats_avg_query\n"; - print "graph_title PgBouncer $pool_name average query time\n"; + print "graph_title PgBouncer $plugin_title average query time\n"; print "graph_args --base 1000\n"; # numbers not bytes print "graph_vlabel Average time per query (microseconds)\n"; - print "graph_category pgbouncer\n"; + print "graph_category db\n"; print $pool_name."_avg_query.type GAUGE\n"; print $pool_name."_avg_query.label Avg Time\n"; print $pool_name."_avg_query.min 0\n"; print $pool_name."_avg_query.draw LINE2\n"; # stats: in/out bytes print "multigraph ".$plugin_name."_stats_bytesinout\n"; - print "graph_title PgBouncer $pool_name average bytes received/sent\n"; + print "graph_title PgBouncer $plugin_title average bytes received/sent\n"; print "graph_args --base 1024\n"; # numbers in bytes print "graph_vlabel Average bytes received (-)/sent (+)\n"; - print "graph_category pgbouncer\n"; + print "graph_category db\n"; # bytes received print $pool_name."_avg_recv.type GAUGE\n"; print $pool_name."_avg_recv.label Avg received\n"; @@ -92,8 +99,8 @@ if (defined($ARGV[0])) print $pool_name."_avg_sent.negative ".$pool_name."_avg_recv\n"; # pools: server (sv_) print "multigraph ".$plugin_name."_pools_server\n"; - print "graph_title PgBouncer $pool_name servers\n"; - print "graph_category pgbouncer\n"; + print "graph_title PgBouncer $plugin_title servers\n"; + print "graph_category db\n"; print "graph_args --base 1000\n"; # numbers not bytes print "graph_vlabel Server connections\n"; print "graph_scale no\n"; @@ -124,8 +131,8 @@ if (defined($ARGV[0])) print $pool_name."_server_login.draw STACK\n"; # pools: client (cl_) print "multigraph ".$plugin_name."_pools_client\n"; - print "graph_title PgBouncer $pool_name clients\n"; - print "graph_category pgbouncer\n"; + print "graph_title PgBouncer $plugin_title clients\n"; + print "graph_category db\n"; print "graph_args --base 1000\n"; # numbers not bytes print "graph_vlabel Client connections\n"; print "graph_scale no\n"; @@ -141,10 +148,10 @@ if (defined($ARGV[0])) print $pool_name."_client_waiting.draw STACK\n"; # pools: maxwait (longest waiting connection, should be 0) print "multigraph ".$plugin_name."_pools_maxwait\n"; - print "graph_title PgBouncer $pool_name maximum waiting time\n"; + print "graph_title PgBouncer $plugin_title maximum waiting time\n"; print "graph_args --base 1000\n"; # numbers not bytes print "graph_vlabel Maximum wait time (seconds)\n"; - print "graph_category pgbouncer\n"; + print "graph_category db\n"; print $pool_name."_maxwait.type GAUGE\n"; print $pool_name."_maxwait.label Wait Time\n"; print $pool_name."_maxwait.min 0\n"; @@ -219,6 +226,7 @@ perl and DBD::Pg is required, and pgbouncer must been installed with a correct s =head1 CONFIGURATION the plugin that will be run needs to have the pool name after the plugin base name. +alternatively, pool name can be specified in config file as env.pgbouncer_pool option, separating plugin name from pool name. =head2 plugin configuration @@ -241,6 +249,21 @@ more extended would be: env.pgbouncer_port 6542 env.pgbouncer_host localhost +another example, where different pgbouncers (and so munin plugins) connecting to same db: + [pgbouncer_weblogin] + env.pgbouncer_pass barfoo + env.pgbouncer_user bar + env.pgbouncer_port 6542 + env.pgbouncer_host localhost + env.pgbouncer_pool dbname + + [pgbouncer_webmain] + env.pgbouncer_pass barfoo + env.pgbouncer_user bar + env.pgbouncer_port 6543 + env.pgbouncer_host localhost + env.pgbouncer_pool dbname + The database name is always pgbouncer =head1 OUTPUT diff --git a/plugins/postgresql/pgbouncer_client_connections b/plugins/postgresql/pgbouncer_client_connections index 5283d8ab..0ba6e0ec 100755 --- a/plugins/postgresql/pgbouncer_client_connections +++ b/plugins/postgresql/pgbouncer_client_connections @@ -26,7 +26,7 @@ cmd="psql ${dbserver} -U ${dbuser} -p ${port} pgbouncer -tc 'SHOW POOLS;' | grep if [ "$1" = "config" ]; then echo 'graph_args --lower-limit 0' - echo 'graph_category pgbouncer' + echo 'graph_category db' echo 'graph_info The sum of the active and waiting clients for each pool on the server.' echo 'graph_scale no' echo 'graph_title PgBouncer Pool Total Client Connections' @@ -46,11 +46,9 @@ if [ "$1" = "config" ]; then echo ${pool}.type GAUGE done - # If dirty config capability is enabled then fall through - # to output the data with the config information. - if [ "$MUNIN_CAP_DIRTYCONFIG" = "" ]; then - exit 0 - fi + # If dirty config capability is enabled then fall through + # to output the data with the config information. + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" != "1" ]; then exit 0; fi fi # Output looks like this: diff --git a/plugins/postgresql/pgbouncer_maxwait b/plugins/postgresql/pgbouncer_maxwait index b6091363..a257ec6d 100755 --- a/plugins/postgresql/pgbouncer_maxwait +++ b/plugins/postgresql/pgbouncer_maxwait @@ -26,7 +26,7 @@ cmd="psql ${dbserver} -U ${dbuser} -p ${port} pgbouncer -tc 'SHOW POOLS;' | grep if [ "$1" = "config" ]; then echo 'graph_args --lower-limit 0' - echo 'graph_category pgbouncer' + echo 'graph_category db' echo 'graph_info PgBouncer Pool Maximum Wait' echo 'graph_scale no' echo 'graph_title PgBouncer Pool Maximum Wait' @@ -46,11 +46,9 @@ if [ "$1" = "config" ]; then echo ${pool}.type GAUGE done - # If dirty config capability is enabled then fall through - # to output the data with the config information. - if [ "$MUNIN_CAP_DIRTYCONFIG" = "" ]; then - exit 0 - fi + # If dirty config capability is enabled then fall through + # to output the data with the config information. + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" != "1" ]; then exit 0; fi fi # Output looks like this: diff --git a/plugins/postgresql/pgbouncer_server_connections b/plugins/postgresql/pgbouncer_server_connections index f795f47d..6b63d2a9 100755 --- a/plugins/postgresql/pgbouncer_server_connections +++ b/plugins/postgresql/pgbouncer_server_connections @@ -26,7 +26,7 @@ cmd="psql ${dbserver} -U ${dbuser} -p ${port} pgbouncer -tc 'SHOW POOLS;' | grep if [ "$1" = "config" ]; then echo 'graph_args --lower-limit 0' - echo 'graph_category pgbouncer' + echo 'graph_category db' echo 'graph_info The sum of the active and idle server connections for each pool on the server.' echo 'graph_scale no' echo 'graph_title PgBouncer Pool Total Server Connections' @@ -46,11 +46,9 @@ if [ "$1" = "config" ]; then echo ${pool}.type GAUGE done - # If dirty config capability is enabled then fall through - # to output the data with the config information. - if [ "$MUNIN_CAP_DIRTYCONFIG" = "" ]; then - exit 0 - fi + # If dirty config capability is enabled then fall through + # to output the data with the config information. + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" != "1" ]; then exit 0; fi fi # Output looks like this: diff --git a/plugins/postgresql/postgres_block_read_ b/plugins/postgresql/postgres_block_read_ index 704448e9..62a6f4b5 100755 --- a/plugins/postgresql/postgres_block_read_ +++ b/plugins/postgresql/postgres_block_read_ @@ -128,7 +128,7 @@ if ($configure) { graph_title Postgres data reads from $dbname graph_args --base 1000 graph_vlabel Blocks read per \${graph_period} -graph_category Postgresql +graph_category db graph_info Shows number of blocks read from disk and from memory from_disk.label Read from disk from_disk.info Read from disk diff --git a/plugins/postgresql/postgres_locks b/plugins/postgresql/postgres_locks index 001a2a71..3cec06ab 100755 --- a/plugins/postgresql/postgres_locks +++ b/plugins/postgresql/postgres_locks @@ -12,7 +12,7 @@ if ($ARGV[0] && $ARGV[0] eq "config") { graph_title Postgres locks graph_args -l 0 --base 1000 graph_vlabel Locks -graph_category Postgresql +graph_category db graph_info Shows Postgresql locks locks.label Locks locks.info Locks (more info here, please... :) diff --git a/plugins/postgresql/postgres_queries2_ b/plugins/postgresql/postgres_queries2_ index c5a95671..5c8185f2 100755 --- a/plugins/postgresql/postgres_queries2_ +++ b/plugins/postgresql/postgres_queries2_ @@ -14,7 +14,7 @@ cat << EOF graph_title Postgres queries2 on $db graph_args --base 1000 graph_vlabel Queries per \${graph_period} -graph_category PostgreSQL +graph_category db graph_info Shows number of select, insert, update and delete queries sel_seq.label s_selects sel_seq.info Sequential selects on all tables diff --git a/plugins/postgresql/postgres_queries3_ b/plugins/postgresql/postgres_queries3_ index cdefb2f3..6d4b873e 100755 --- a/plugins/postgresql/postgres_queries3_ +++ b/plugins/postgresql/postgres_queries3_ @@ -60,7 +60,7 @@ if( defined $ARGV[0] and $ARGV[0] eq "config" ){ print qq/graph_title Postgres All Queries graph_args --base 1000 -l 0 -r graph_vlabel Queries per \${graph_period} -graph_category PostgreSQL +graph_category db graph_info Shows number of select, insert, update and delete queries sel_seq.label s_selects sel_seq.info Sequential selects on all tables diff --git a/plugins/postgresql/postgres_space_ b/plugins/postgresql/postgres_space_ index 6a8931b0..ed387631 100755 --- a/plugins/postgresql/postgres_space_ +++ b/plugins/postgresql/postgres_space_ @@ -80,7 +80,7 @@ if ($configure) { graph_title Postgres database $dbname graph_args -l 0 --base 1024 graph_vlabel bytes -graph_category Postgresql +graph_category db graph_info Size size.label Database size (bytes) size.info Database size diff --git a/plugins/postgresql/postgres_tuplesratio_ b/plugins/postgresql/postgres_tuplesratio_ old mode 100644 new mode 100755 index 4826c44b..9568770a --- a/plugins/postgresql/postgres_tuplesratio_ +++ b/plugins/postgresql/postgres_tuplesratio_ @@ -77,3 +77,7 @@ my $pg = Munin::Plugin::Pgsql->new( $pg->Process(); exit(0); + +# for Munin Plugin Gallery +# graph_category db + diff --git a/plugins/postgresql/postgresql_active_backends b/plugins/postgresql/postgresql_active_backends index 4b0f9a65..69426d36 100755 --- a/plugins/postgresql/postgresql_active_backends +++ b/plugins/postgresql/postgresql_active_backends @@ -33,7 +33,7 @@ if [ "$1" = "config" ]; then warning=$(((maximum-reserved)*70/100)) critical=$(((maximum-reserved)*90/100)) echo 'graph_args --base 1000 --lower-limit 0 --upper-limit '${maximum} - echo 'graph_category Postgresql' + echo 'graph_category db' echo 'graph_info Shows open backends on the PostgreSQL Server.' echo 'graph_scale no' echo 'graph_title PostgreSQL Active Backends' diff --git a/plugins/postgresql/postgresql_active_backends_by_database b/plugins/postgresql/postgresql_active_backends_by_database index cbd6bb1d..78a34745 100755 --- a/plugins/postgresql/postgresql_active_backends_by_database +++ b/plugins/postgresql/postgresql_active_backends_by_database @@ -34,7 +34,7 @@ if [ "$1" = "config" ]; then critical=$(((maximum-reserved)*90/100)) echo 'graph_args --base 1000 --lower-limit 0' # --upper-limit '${maximum} - echo 'graph_category Postgresql' + echo 'graph_category db' echo 'graph_info Shows open backends for each database on the server' echo 'graph_scale no' echo 'graph_title PostgreSQL Active Backends By Database' @@ -73,9 +73,7 @@ if [ "$1" = "config" ]; then # If dirty config capability is enabled then fall through # to output the data with the config information. - if [ "$MUNIN_CAP_DIRTYCONFIG" = "" ]; then - exit 0 - fi + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" != "1" ]; then exit 0; fi fi while read pool sep backends junk diff --git a/plugins/postgresql/postgresql_active_locks b/plugins/postgresql/postgresql_active_locks index 4c23f3a1..205507cb 100755 --- a/plugins/postgresql/postgresql_active_locks +++ b/plugins/postgresql/postgresql_active_locks @@ -31,7 +31,7 @@ modes="AccessExclusive AccessShare Exclusive RowExclusive RowShare Share ShareRo if [ "$1" = "config" ]; then echo 'graph_args --lower-limit 0' - echo 'graph_category Postgresql' + echo 'graph_category db' echo 'graph_info Shows active locks on database server.' echo 'graph_scale no' echo 'graph_title PostgreSQL Active Locks' diff --git a/plugins/postgresql/postgresql_database_ratio b/plugins/postgresql/postgresql_database_ratio index 792deb66..e1a29a7e 100755 --- a/plugins/postgresql/postgresql_database_ratio +++ b/plugins/postgresql/postgresql_database_ratio @@ -27,7 +27,7 @@ dbuser='postgres' if [ "$1" = "config" ]; then echo 'graph_args --base 1000 --lower-limit 0 --upper-limit 100' - echo 'graph_category Postgresql' + echo 'graph_category db' echo 'graph_info Shows each database read/hit (%) on the PostgreSQL Server.' echo 'graph_scale no' echo 'graph_title PostgreSQL Database Hit/Read Ratios' diff --git a/plugins/postgresql/postgresql_database_size b/plugins/postgresql/postgresql_database_size index 13696097..4c4df292 100755 --- a/plugins/postgresql/postgresql_database_size +++ b/plugins/postgresql/postgresql_database_size @@ -27,7 +27,7 @@ dbuser='postgres' if [ "$1" = "config" ]; then echo 'graph_args --base 1024 --lower-limit 0' - echo 'graph_category Postgresql' + echo 'graph_category db' echo 'graph_info Shows each database size on the PostgreSQL Server.' echo 'graph_title PostgreSQL Database Sizes' echo 'graph_vlabel Size (bytes)' diff --git a/plugins/postgresql/postgresql_tablespace_size b/plugins/postgresql/postgresql_tablespace_size index 3d227992..cae81458 100755 --- a/plugins/postgresql/postgresql_tablespace_size +++ b/plugins/postgresql/postgresql_tablespace_size @@ -26,7 +26,7 @@ dbuser='postgres' if [ "$1" = "config" ]; then echo 'graph_args --base 1024 --lower-limit 0' - echo 'graph_category Postgresql' + echo 'graph_category db' echo 'graph_info Shows each tablespace size on the PostgreSQL Server.' echo 'graph_title PostgreSQL Tablespace Sizes' echo 'graph_vlabel Size (bytes)' diff --git a/plugins/postgresql/postgresql_transactions b/plugins/postgresql/postgresql_transactions index eac9739d..f13740a5 100755 --- a/plugins/postgresql/postgresql_transactions +++ b/plugins/postgresql/postgresql_transactions @@ -26,7 +26,7 @@ dbuser='postgres' if [ "$1" = "config" ]; then echo 'graph_args --base 1000 --lower-limit 0' - echo 'graph_category Postgresql' + echo 'graph_category db' echo 'graph_info Shows summarized commits and rollbacks in transactions on the PostgreSQL Server.' echo 'graph_title PostgreSQL Transactions' echo 'graph_vlabel Number of Commits and Rollbacks' @@ -40,4 +40,4 @@ if [ "$1" = "config" ]; then echo 'rollbacks.info Number of transaction rollbacks.' exit 0 fi -psql -h ${dbserver} -U ${dbuser} -tc "SELECT '\ncommits.value '||SUM(xact_commit)::TEXT || '\nrollbacks.value '||SUM(xact_rollback)::TEXT FROM pg_stat_database;" +psql -h ${dbserver} -U ${dbuser} -tc "SELECT 'commits.value '||SUM(xact_commit)::TEXT||E'\nrollbacks.value '||SUM(xact_rollback)::TEXT FROM pg_stat_database;" --no-align diff --git a/plugins/postgresql/slony_ b/plugins/postgresql/slony_ index f1e725c6..0358833a 100755 --- a/plugins/postgresql/slony_ +++ b/plugins/postgresql/slony_ @@ -70,7 +70,7 @@ if ($ARGV[0] && $ARGV[0] eq "config") { graph_title Slony latency graph_args --base 1000 graph_vlabel Latency in seconds -graph_category PostgreSQL +graph_category db graph_info Shows Slony latency EOF my $sql="select no_id,no_comment,pa_conninfo from $cluster.sl_node join $cluster.sl_path on (pa_server=no_id) where pa_client= $cluster.getlocalnodeid('$cluster'::name);"; diff --git a/plugins/slony/slony_lag_events_ b/plugins/postgresql/slony_lag_events_ similarity index 98% rename from plugins/slony/slony_lag_events_ rename to plugins/postgresql/slony_lag_events_ index 1e2a72ca..13d88f83 100755 --- a/plugins/slony/slony_lag_events_ +++ b/plugins/postgresql/slony_lag_events_ @@ -49,7 +49,7 @@ PGDATABASE=$(basename $0 | sed 's/^slony_lag_events_//g') if [ "$1" = "config" ]; then echo "graph_args --base 1000 -l 0" - echo "graph_category slony" + echo "graph_category db" echo "graph_info Slony st_lag_num_events for ${PGDATABASE}" echo "graph_title Slony lag events for ${PGDATABASE}" echo 'graph_vlabel event' diff --git a/plugins/slony/slony_lag_time b/plugins/postgresql/slony_lag_time similarity index 98% rename from plugins/slony/slony_lag_time rename to plugins/postgresql/slony_lag_time index fafee9be..3ba34422 100755 --- a/plugins/slony/slony_lag_time +++ b/plugins/postgresql/slony_lag_time @@ -49,7 +49,7 @@ PGDATABASE=$(basename $0 | sed 's/^slony_lag_time_//g') if [ "$1" = "config" ]; then echo "graph_args --base 1000 -l 0" - echo "graph_category slony" + echo "graph_category db" echo "graph_info Slony st_lag_time for ${PGDATABASE}" echo "graph_title Slony lag time for ${PGDATABASE}" echo "graph_vlabel \${graph_period}" diff --git a/plugins/slony/slony_lag_time_ b/plugins/postgresql/slony_lag_time_ similarity index 98% rename from plugins/slony/slony_lag_time_ rename to plugins/postgresql/slony_lag_time_ index 223e840b..c671f657 100755 --- a/plugins/slony/slony_lag_time_ +++ b/plugins/postgresql/slony_lag_time_ @@ -49,7 +49,7 @@ PGDATABASE=$(basename $0 | sed 's/^slony_lag_time_//g') if [ "$1" = "config" ]; then echo "graph_args --base 1000 -l 0" - echo "graph_category slony" + echo "graph_category db" echo "graph_info Slony st_lag_time for ${PGDATABASE}" echo "graph_title Slony lag time for ${PGDATABASE}" echo "graph_vlabel \${graph_period}" diff --git a/plugins/ups/apc_status b/plugins/power/apc_status similarity index 95% rename from plugins/ups/apc_status rename to plugins/power/apc_status index 9860dd24..fcbb9344 100755 --- a/plugins/ups/apc_status +++ b/plugins/power/apc_status @@ -18,7 +18,7 @@ if [ "$1" = "config" ]; then echo 'multigraph apc_status' echo "graph_title UPS Status - $title" echo 'graph_args --base 1000' - echo 'graph_category hardware' + echo 'graph_category sensors' title=`/sbin/apcaccess | egrep "^MODEL" | awk '{print $3" "$4" "$5" "$6" "$7" "$8" "$9;}'` echo "graph_info $title" for key in $keys; do @@ -35,7 +35,7 @@ if [ "$1" = "config" ]; then if [ -n "$unit" ]; then echo "graph_vlabel $unit" fi - echo 'graph_category hardware' + echo 'graph_category sensors' echo "$key.label $key" echo "$key.info $key." echo "$key.draw LINE1" diff --git a/plugins/ups/apcupsd_pct b/plugins/power/apcupsd_pct similarity index 99% rename from plugins/ups/apcupsd_pct rename to plugins/power/apcupsd_pct index 65e34ea5..dec45cfb 100755 --- a/plugins/ups/apcupsd_pct +++ b/plugins/power/apcupsd_pct @@ -64,7 +64,7 @@ sub decide_monitor_type { # common %Graph = ( graph_title => "APC Status".($UPS_MODEL?" ($UPS_MODEL)":"")." - ", - graph_category => "ups", + graph_category => "sensors", graph_info => "This graph shows information about your APC UPS", graph_args => "--base 1000 --lower-limit 0", ); diff --git a/plugins/ups/apcupsd_ww b/plugins/power/apcupsd_ww similarity index 99% rename from plugins/ups/apcupsd_ww rename to plugins/power/apcupsd_ww index b0b86f7c..b50067b3 100755 --- a/plugins/ups/apcupsd_ww +++ b/plugins/power/apcupsd_ww @@ -204,7 +204,7 @@ if (defined $ARGV[0] && $ARGV[0] eq 'config') { print "graph_title $data{MODEL}\n"; } #print "graph_vlabel Units\n"; - print "graph_category ups\n"; + print "graph_category sensors\n"; print "graph_info This graph shows information about your APC uninterruptible power supply.\n"; foreach my $what (sort keys %attrs) { diff --git a/plugins/currentcost/currentcost b/plugins/power/currentcost similarity index 100% rename from plugins/currentcost/currentcost rename to plugins/power/currentcost diff --git a/plugins/ups/eatonups_ b/plugins/power/eatonups_ old mode 100644 new mode 100755 similarity index 100% rename from plugins/ups/eatonups_ rename to plugins/power/eatonups_ diff --git a/plugins/ups/nut b/plugins/power/nut similarity index 100% rename from plugins/ups/nut rename to plugins/power/nut diff --git a/plugins/ups/nutups2_ b/plugins/power/nutups2_ similarity index 100% rename from plugins/ups/nutups2_ rename to plugins/power/nutups2_ diff --git a/plugins/snmp/snmp__apc_pdu b/plugins/power/snmp__apc_pdu similarity index 100% rename from plugins/snmp/snmp__apc_pdu rename to plugins/power/snmp__apc_pdu diff --git a/plugins/snmp/snmp__apc_ups b/plugins/power/snmp__apc_ups similarity index 99% rename from plugins/snmp/snmp__apc_ups rename to plugins/power/snmp__apc_ups index bbcc77c6..73489c8f 100755 --- a/plugins/snmp/snmp__apc_ups +++ b/plugins/power/snmp__apc_ups @@ -463,7 +463,7 @@ sub emit_config { say "graph_title ".( $title{$graph} || "Untitled /$graph/"); say process_oids( "graph_vlabel " . $vlabel{$graph}, $values ); - say "graph_category UPS"; + say "graph_category sensors"; say process_oids( "graph_info " . $info{$graph}, $values ); foreach my $label (keys %{ $label{$graph} } ) { diff --git a/plugins/snmp/snmp__ipoman_ b/plugins/power/snmp__ipoman_ similarity index 98% rename from plugins/snmp/snmp__ipoman_ rename to plugins/power/snmp__ipoman_ index 322909d0..9524460b 100755 --- a/plugins/snmp/snmp__ipoman_ +++ b/plugins/power/snmp__ipoman_ @@ -217,7 +217,7 @@ if (defined $ARGV[0] and $ARGV[0] eq "config") { print "graph_title Inlet $socketnumber voltage/frequency\n"; print "graph_args --base 1000 -l 0\n"; - print "graph_category system\n"; + print "graph_category sensors\n"; print "graph_info This graph shows the tension and frequency to inlet $socketnumber on the Power Distribution Unit\n"; print "voltage.label Tension (V)\n"; @@ -233,7 +233,7 @@ if (defined $ARGV[0] and $ARGV[0] eq "config") { print "graph_title Inlet $socketnumber current\n"; print "graph_args --base 1000 -l 0\n"; - print "graph_category system\n"; + print "graph_category sensors\n"; print "graph_info This graph shows the delivered current to inlet $socketnumber on the Power Distribution Unit\n"; print "current.label Current (A)\n"; @@ -245,7 +245,7 @@ if (defined $ARGV[0] and $ARGV[0] eq "config") { print "graph_title Inlet $socketnumber power\n"; print "graph_args --base 1000 -l 0\n"; - print "graph_category system\n"; + print "graph_category sensors\n"; print "graph_info This graph shows the delivered apparent and real power to inlet $socketnumber of the Power Distribution Unit\n"; print "apparentpower.label Apparent power (kVA)\n"; @@ -262,7 +262,7 @@ if (defined $ARGV[0] and $ARGV[0] eq "config") { print "graph_title Outlet $socketnumber current\n"; print "graph_args --base 1000 -l 0\n"; - print "graph_category system\n"; + print "graph_category sensors\n"; print "graph_info This graph shows the delivered current to outlet $socketnumber of the Power Distribution Unit\n"; print "current.label Delivered current (A)\n"; @@ -273,7 +273,7 @@ if (defined $ARGV[0] and $ARGV[0] eq "config") { print "graph_title Outlet $socketnumber power\n"; print "graph_args --base 1000 -l 0\n"; - print "graph_category system\n"; + print "graph_category sensors\n"; print "graph_info This graph shows the delivered apparent and real power to outlet $socketnumber of the Power Distribution Unit\n"; print "apparentpower.label Apparent power (kVA)\n"; diff --git a/plugins/snmp/snmp__sentry b/plugins/power/snmp__sentry similarity index 99% rename from plugins/snmp/snmp__sentry rename to plugins/power/snmp__sentry index 05456754..4631b0c4 100755 --- a/plugins/snmp/snmp__sentry +++ b/plugins/power/snmp__sentry @@ -143,7 +143,7 @@ multigraph power_amps_drawn graph_title Power Draw in Amps graph_args --lower-limit 0 graph_vlabel Amps -graph_category pdu +graph_category sensors graph_scale no graph_info This shows the amperage drawn on your PDU. Per NEC, a PDU should not sustain 80% of its maximum circuit capacity for more than three hours. @@ -167,7 +167,7 @@ multigraph power_power_factor graph_title Power Factor graph_args --lower-limit 0 graph_vlabel Power Factor -graph_category pdu +graph_category sensors graph_scale no graph_info Power factor represents the efficiency of the components connected to the circuit. Power factor declines as components age, increasing the overall load on the circuit. @@ -186,7 +186,7 @@ multigraph power_crest_factor graph_title Crest Factor graph_args --lower-limit 0 graph_vlabel Crest Factor -graph_category pdu +graph_category sensors graph_scale no graph_info Crest factor relates the peak value of a signal to its root mean square value. For three-phase AC, a crest factor of 1.732 is expected. Low crest factor may indicate UPS overclipping and crest factors over 5 degrade monitoring accuracy. diff --git a/plugins/snmp/snmp__ups_ b/plugins/power/snmp__ups_ similarity index 100% rename from plugins/snmp/snmp__ups_ rename to plugins/power/snmp__ups_ diff --git a/plugins/power/upsmonpro_ b/plugins/power/upsmonpro_ new file mode 100755 index 00000000..0008e4e2 --- /dev/null +++ b/plugins/power/upsmonpro_ @@ -0,0 +1,222 @@ +#!/usr/bin/perl + +=head1 NAME + +upsmonpro_ - Munin plugin to monitor Powercom UPS via UPSMON PRO program L + +=head1 INSTALLATION + + /etc/munin/plugins/upsmonpro_load -> /usr/share/munin/plugins/upsmonpro_ + /etc/munin/plugins/upsmonpro_status -> /usr/share/munin/plugins/upsmonpro_ + /etc/munin/plugins/upsmonpro_temp -> /usr/share/munin/plugins/upsmonpro_ + /etc/munin/plugins/upsmonpro_voltage -> /usr/share/munin/plugins/upsmonpro_ + +=head1 CONFIGURATION + +Environment variables: + + host - UPSMON PRO server host, default localhost + port - UPSMON PRO port, default 2601 + +Example configuration (optional): + + [upsmonpro_*] + env.host localhost + env.port 2601 + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + #%# capabilities=suggest + +=head1 AUTHOR + +Copyright (C) 2017 pru.mike@gmail.com + +=head1 LICENSE + +GPLv2. + +=cut + +use strict; +use warnings; +use feature qw/say/; +use Munin::Plugin; +use IO::Socket::INET; +use Time::HiRes qw/usleep/; +use English qw/-no-math-vars/; +our $VERSION = '0.0.1'; +$OUTPUT_AUTOFLUSH++; + +our $DEFAULT_HOST = 'localhost'; +our $DEFAULT_PORT = 2601; +our %TYPES = ( + voltage => [qw/input output/], + load => [qw/battery_load battery_capacity/], + temp => [q/temp/], + status => [qw/power_failure low_battery voltage_status ups_status battery_test/] +); +our @TYPES = sort keys %TYPES; +my %DISPATCH_TABLE = (); +my $pkg = __PACKAGE__; + +$DISPATCH_TABLE{"${pkg}::run_suggest"} = \&run_suggest; +$DISPATCH_TABLE{"${pkg}::run_autoconf"} = \&run_autoconf; +for my $t (@TYPES) { + $DISPATCH_TABLE{"${pkg}::run_config_$t"} = \&{"run_config_$t"}; + $DISPATCH_TABLE{"${pkg}::run_autoconf_$t"} = \&run_autoconf; + $DISPATCH_TABLE{"${pkg}::run_default_$t"} = sub { + run_default(@{ $TYPES{$t} }); + }; +} + +find_key($ARGV[0])->(); + +sub find_key { + my $argv = shift || ''; + my $type_re = join '|', @TYPES; + my $key; + if ($argv =~ /(suggest|autoconf)/i) { + $key = 'run_' . lc($1); + } elsif ($Munin::Plugin::me =~ /upsmonpro_{1,}($type_re)$/) { + my $graph = $1; + $key = 'run_' . ((grep { $argv eq $_ } qw/autoconf config/) ? $argv : 'default') . "_$graph"; + } else { + die "Could not determine script type [@TYPES] ? name=$Munin::Plugin::me\n"; + } + + return $DISPATCH_TABLE{"${pkg}::$key"}; +} + +sub run_config_voltage { + print <<'END'; +graph_title UPS Input/Output Voltage +graph_vlabel volt +graph_scale no +graph_category sensors +input.label input +input.info Input Voltage +input.type GAUGE +output.label output +output.info Output Voltage +output.type GAUGE +END + exit(0); +} + +sub run_config_temp { + print <<'END'; +graph_title UPS Temperature +graph_vlabel celsius +graph_scale no +graph_category sensors +temp.label temperature +temp.type GAUGE +END + exit(0); +} + +sub run_config_load { + print <<'END'; +graph_title UPS Battery Load/Capacity +graph_vlabel precent% +graph_scale no +graph_category sensors +battery_load.label battery_load +battery_load.type GAUGE +battery_capacity.label battery_capacity +battery_capacity.type GAUGE +END + exit(0); +} + +sub run_config_status { + print <<'END'; +graph_title UPS Statuses +graph_vlabel status +graph_scale no +graph_category sensors +power_failure.label power_failure +power_failure.type GAUGE +low_battery.label low_battery +low_battery.type GAUGE +voltage_status.label voltage_status +voltage_status.info 0 normal, 1 boost, 2 buck +voltage_status.type GAUGE +ups_status.label ups_status +ups_status.type GAUGE +battery_test.label battery_test +battery_test.type GAUGE +END + exit(0); +} + +sub run_default { + my $host = $ENV{host} || $DEFAULT_HOST; + my $port = $ENV{port} || $DEFAULT_PORT; + my @val_list = @_; + my $r = gather_data($host, $port); + for (@val_list) { + die "Wrong value: $_" if not exists $r->{$_}; + say "${_}.value $r->{$_}"; + } +} + +sub run_suggest { + local $LIST_SEPARATOR = "\n"; + say "@TYPES"; + exit(0); +} + +sub run_autoconf { + if (gather_data($DEFAULT_HOST, $DEFAULT_PORT)->{response} eq 'ok') { + say "yes"; + } else { + say "no ($DEFAULT_HOST:$DEFAULT_PORT not response)"; + } + exit(0); +} + +sub gather_data { + my $host = shift; + my $port = shift; + my %data = map { ($_ => 'U') } map { @{ $TYPES{$_} } } @TYPES; + $data{response} = 'failed'; + + my $sock = IO::Socket::INET->new( + PeerAddr => $host, + Proto => 'udp', + PeerPort => $port, + Blocking => 0 + ) or die "Cannot create socket: $@"; + + my $pattern = pack 'AAAACAAA', split //, 'PCMG1END'; + $sock->send($pattern) or die "send to $host: $!"; + usleep(200); + my $data; + my $buf; + while ($sock->read($buf, 32)) { + $data .= $buf; + } + if (defined $data and $data =~ /^PCMR.*END$/) { + my @data = unpack('C*', $data); + %data = ( + response => 'ok', + input => $data[6] + 256 * $data[5], + output => $data[8] + 256 * $data[7], + battery_load => $data[11], + battery_capacity => $data[12], + temp => $data[15], + power_failure => ($data[18] ? 1 : 0), + low_battery => ($data[19] ? 1 : 0), + + #voltage_status: 0 = normal, 3 = buck, 2 = boost + voltage_status => ($data[17] == 0 ? 0 : ($data[17] == 3 ? 2 : 1)), + ups_status => ($data[21] ? 1 : 0), + battery_test => ($data[23] ? 1 : 0), + ); + } + return \%data; +} diff --git a/plugins/power5/consumed_cpu_cycles b/plugins/power5/consumed_cpu_cycles index e8c8e1c8..950c31a6 100755 --- a/plugins/power5/consumed_cpu_cycles +++ b/plugins/power5/consumed_cpu_cycles @@ -29,8 +29,7 @@ LPARCFG=/proc/ppc64/lparcfg -# STATEFILE=/var/lib/munin/plugin-state/power5-consumed_cpu_cycles.state -STATEFILE=/tmp/power5-consumed_cpu_cycles.state +STATEFILE=$MUNIN_PLUGSTATE/power5-consumed_cpu_cycles.state # check input parameters if [ "$1" = "autoconf" ]; then echo yes @@ -40,7 +39,7 @@ fi if [ "$1" = "config" ]; then echo 'graph_title Consumed CPU cycles' echo 'graph_args -l 0' - echo 'graph_category Power5' + echo 'graph_category cpu' echo 'graph_vlabel CPU cycles' echo 'graph_info This graph shows the CPU cycles on an uncapped LPAR' @@ -79,7 +78,7 @@ else OLDPURR=$ACTUALPURR; fi -# calcualte CPU cycles +# calculate CPU cycles CPUCYCLES=$(echo $OLDPURR $ACTUALPURR $TIMEBASE $OLDTIME $ACTUALTIME | awk '{printf ("%.1f", (($1-$2)/$3)/($4-$5));}'); # write data to state file diff --git a/plugins/power5/cpu_entitlemens b/plugins/power5/cpu_entitlemens index 5afba7dd..7b8c9675 100755 --- a/plugins/power5/cpu_entitlemens +++ b/plugins/power5/cpu_entitlemens @@ -39,7 +39,7 @@ fi if [ "$1" = "config" ]; then echo 'graph_title CPU entitlements' echo 'graph_args -l 0' - echo 'graph_category Power5' + echo 'graph_category cpu' echo 'graph_vlabel entitlements' echo 'graph_info This graph shows the actual and max entitlements for a LPAR' diff --git a/plugins/power5/cpu_in_lpar b/plugins/power5/cpu_in_lpar index 8de54975..aab20829 100755 --- a/plugins/power5/cpu_in_lpar +++ b/plugins/power5/cpu_in_lpar @@ -38,7 +38,7 @@ fi if [ "$1" = "config" ]; then echo 'graph_title CPU in LPAR' echo 'graph_args -l 0' - echo 'graph_category Power5' + echo 'graph_category cpu' echo 'graph_vlabel CPUs in LPAR' echo 'graph_info This graph shows potential and active processors for a LPAR.' diff --git a/plugins/power5/weight_of_a_lpar b/plugins/power5/weight_of_a_lpar index d451972b..2d2e2b05 100755 --- a/plugins/power5/weight_of_a_lpar +++ b/plugins/power5/weight_of_a_lpar @@ -38,7 +38,7 @@ fi if [ "$1" = "config" ]; then echo 'graph_title Weight of an uncapped LPAR' echo 'graph_args -l 0' - echo 'graph_category Power5' + echo 'graph_category cpu' echo 'graph_vlabel Weight of LPAR' echo 'graph_info This graph shows the weight of an uncapped LPAR' echo 'weight.min 0' diff --git a/plugins/powermta/pmta_ b/plugins/powermta/pmta_ index 73fb1e7e..cace6ce9 100755 --- a/plugins/powermta/pmta_ +++ b/plugins/powermta/pmta_ @@ -225,7 +225,7 @@ case $1 in ;; config) if [ -n "$CONF_TITLE" ] && [ -n "$CONF_LABEL" ]; then - echo "graph_category PowerMTA" + echo "graph_category mail" echo "graph_title $CONF_TITLE on ${REMOTEHOST}" echo "graph_vlabel $CONF_LABEL" echo "graph_scale yes" diff --git a/plugins/powermta/powermta_vmta_recpients b/plugins/powermta/powermta_vmta_recpients index f873dc99..f4fb697e 100755 --- a/plugins/powermta/powermta_vmta_recpients +++ b/plugins/powermta/powermta_vmta_recpients @@ -16,7 +16,7 @@ case $1 in echo "graph_title RGMTA2 PMTA VMTA $vmta Recipents" echo "graph_vlabel recipents" echo "graph_scale no" -echo "graph_category PowerMTA" +echo "graph_category mail" for i in ${queues[@]};do queue=(`pmta show queue $i`) unset queue[0] diff --git a/plugins/printer/dell_5310n_health_ b/plugins/printer/dell_5310n_health_ index 5d401734..8494496c 100755 --- a/plugins/printer/dell_5310n_health_ +++ b/plugins/printer/dell_5310n_health_ @@ -12,7 +12,7 @@ if [ "$1" = "config" ]; then echo "graph_title dell 5310n health: $destination" echo 'graph_vlabel count' echo 'graph_args --lower-limit 0' - echo 'graph_category printer' + echo 'graph_category printing' echo "toner.label Toner (percent)" else diff --git a/plugins/printer/dell_5310n_pages_ b/plugins/printer/dell_5310n_pages_ index 3c7fa382..59047f22 100755 --- a/plugins/printer/dell_5310n_pages_ +++ b/plugins/printer/dell_5310n_pages_ @@ -17,7 +17,7 @@ if [ "$1" = "config" ]; then echo "graph_title dell 5310n pages: $destination" echo 'graph_vlabel count' echo 'graph_args --lower-limit 0' - echo 'graph_category printer' + echo 'graph_category printing' n=1 for t in $trays ; diff --git a/plugins/printer/hp2600_count_ b/plugins/printer/hp2600_count_ index 00f10465..185db3de 100755 --- a/plugins/printer/hp2600_count_ +++ b/plugins/printer/hp2600_count_ @@ -31,7 +31,7 @@ case $1 in host_name printers graph_title HP 2600 pages by cartridge statistics graph_vlabel Count (Pages) -graph_category printer +graph_category printing graph_info Pages count by color. graph_args -l 0 graph_scale no diff --git a/plugins/printer/hp2600_status_ b/plugins/printer/hp2600_status_ index 1d1ed15c..dcb36a24 100755 --- a/plugins/printer/hp2600_status_ +++ b/plugins/printer/hp2600_status_ @@ -31,7 +31,7 @@ case $1 in host_name printers graph_title HP 2600 cartridge status graph_vlabel Status (%) -graph_category printer +graph_category printing graph_info Toner status. graph_args --upper-limit 400 -l 0 line.label --- diff --git a/plugins/printer/oki_c5500_health_ b/plugins/printer/oki_c5500_health_ index 59a7138c..5393fdc6 100755 --- a/plugins/printer/oki_c5500_health_ +++ b/plugins/printer/oki_c5500_health_ @@ -10,7 +10,7 @@ if [ "$1" = "config" ]; then echo "graph_title oki c5500 health: $destination" echo 'graph_vlabel percent' echo 'graph_args --lower-limit 0 --upper-limit 100' - echo 'graph_category printer' + echo 'graph_category printing' echo "tonerCyan.label Toner: Cyan" echo "tonerMagenta.label Toner: Magenta" diff --git a/plugins/printer/oki_c5500_pages_ b/plugins/printer/oki_c5500_pages_ index 75085545..5e8bbec8 100755 --- a/plugins/printer/oki_c5500_pages_ +++ b/plugins/printer/oki_c5500_pages_ @@ -10,7 +10,7 @@ if [ "$1" = "config" ]; then echo "graph_title oki c5500 pages: $destination" echo 'graph_vlabel count' echo 'graph_args --lower-limit 0' - echo 'graph_category printer' + echo 'graph_category printing' echo "tray1.label Tray 1" echo "tray2.label Tray 2" diff --git a/plugins/printer/snmp__hpclj b/plugins/printer/snmp__hpclj new file mode 100755 index 00000000..ffa6083d --- /dev/null +++ b/plugins/printer/snmp__hpclj @@ -0,0 +1,224 @@ +#!/usr/bin/perl -w + +=head1 NAME + +Monitor Consumables of HP Color LaserJet Printers. +Should also work on non-Color LaserJet Printers though. + +=head1 AUTHOR + +Kai Boenke + +=head1 LICENSE + +Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0) + +=back + + +##### +# Enable SNMP-Discovery +### +=head1 MAGIC MARKERS + #%# family=snmpauto + #%# capabilities=snmpconf +=cut +if (defined $ARGV[0] and $ARGV[0] eq "snmpconf") { + print "require 1.3.6.1.2.1.43.11.1.1.9.1.1\n"; + exit 0; +} + + +##### +# Initialize +### +use strict; +use Munin::Plugin::SNMP; +my $session = Munin::Plugin::SNMP->session(); + + +##### +# Declare OIDs +### +use constant oid_black_max => ".1.3.6.1.2.1.43.11.1.1.8.1.1"; +use constant oid_black_cur => ".1.3.6.1.2.1.43.11.1.1.9.1.1"; +use constant oid_cyan_max => ".1.3.6.1.2.1.43.11.1.1.8.1.2"; +use constant oid_cyan_cur => ".1.3.6.1.2.1.43.11.1.1.9.1.2"; +use constant oid_magenta_max => ".1.3.6.1.2.1.43.11.1.1.8.1.3"; +use constant oid_magenta_cur => ".1.3.6.1.2.1.43.11.1.1.9.1.3"; +use constant oid_yellow_max => ".1.3.6.1.2.1.43.11.1.1.8.1.4"; +use constant oid_yellow_cur => ".1.3.6.1.2.1.43.11.1.1.9.1.4"; +use constant oid_tray1_max => ".1.3.6.1.2.1.43.8.2.1.9.1.1"; +use constant oid_tray1_cur => ".1.3.6.1.2.1.43.8.2.1.10.1.1"; +use constant oid_tray2_max => ".1.3.6.1.2.1.43.8.2.1.9.1.2"; +use constant oid_tray2_cur => ".1.3.6.1.2.1.43.8.2.1.10.1.2"; +use constant oid_tray3_max => ".1.3.6.1.2.1.43.8.2.1.9.1.3"; +use constant oid_tray3_cur => ".1.3.6.1.2.1.43.8.2.1.10.1.3"; +use constant oid_pagecount_total => ".1.3.6.1.2.1.43.10.2.1.4.1.1"; + + +##### +# Config +### +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 "multigraph hpclj +graph_category printing +graph_title HP Printer Consumption Levels +graph_info This graph shows Consumption-Levels on HP Printers. +graph_vlabel % +graph_args --upper-limit 100 -l 0 +graph_scale no +"; + print "black.label Black Toner Level +black.draw LINE2 +black.type GAUGE +black.colour 000000 +black.warning 10: +black.critical 3: +black.min 0 +black.max 100 +"; + if(oidExists(oid_cyan_max)){ + print "cyan.label Cyan Toner Level +cyan.draw LINE2 +cyan.type GAUGE +cyan.colour 00FFFF +cyan.warning 5: +cyan.critical 1: +cyan.min 0 +cyan.max 100 +"; + } + if(oidExists(oid_magenta_max)){ + print "magenta.label Magenta Toner Level +magenta.draw LINE2 +magenta.type GAUGE +magenta.colour FF00FF +magenta.warning 5: +magenta.critical 1: +magenta.min 0 +magenta.max 100 +"; + } + if(oidExists(oid_yellow_max)){ + print "yellow.label Yellow Toner Level +yellow.draw LINE2 +yellow.type GAUGE +yellow.colour FFFF00 +yellow.warning 5: +yellow.critical 1: +yellow.min 0 +yellow.max 100 +"; + } + if(oidExists(oid_tray1_max)){ + print "tray1.label Tray1 Fill Level +tray1.draw LINE1 +tray1.type GAUGE +tray1.colour 333333 +tray1.min 0 +tray1.max 100 +"; + } + if(oidExists(oid_tray2_max)){ + print "tray2.label Tray2 Fill Level +tray2.draw LINE1 +tray2.type GAUGE +tray2.colour 666666 +tray2.min 0 +tray2.max 100 +"; + } + if(oidExists(oid_tray3_max)){ + print "tray3.label Tray3 Fill Level +tray3.draw LINE1 +tray3.type GAUGE +tray3.colour 999999 +tray3.min 0 +tray3.max 100 +"; + } + + print "multigraph hpclj_pagecount +graph_category printing +graph_title HP Printer Page Counters +graph_info This graph shows Page-Counters for HP Printers. +graph_vlabel Pages +graph_args --base 1000 -l 0 +"; + print "pagecount.label Printouts +pagecount.draw AREA +pagecount.colour 000000 +"; + exit 0; +} + + +##### +# Get Values +### +print "multigraph hpclj\n"; +printPercentageValue("black", oid_black_cur, oid_black_max); +printPercentageValue("cyan", oid_cyan_cur, oid_cyan_max); +printPercentageValue("magenta", oid_magenta_cur, oid_magenta_max); +printPercentageValue("yellow", oid_yellow_cur, oid_yellow_max); +printPercentageValue("tray1", oid_tray1_cur, oid_tray1_max); +printPercentageValue("tray2", oid_tray2_cur, oid_tray2_max); +printPercentageValue("tray3", oid_tray3_cur, oid_tray3_max); + +print "multigraph hpclj_pagecount\n"; +printValue("pagecount", oid_pagecount_total); + + +##### +# Subroutines +### +sub printPercentageValue { + if(not defined $_[0] || not defined $_[1] || not defined $_[2]) { + exit 0; + } + my $field = $_[0]; + my $oid_cur = $_[1]; + my $oid_max = $_[2]; + + if(not oidExists($oid_cur) || not oidExists($oid_max)){ + return(0); + } + + my $val_max = $session->get_single($oid_max) || 'U'; + my $val_cur = $session->get_single($oid_cur); + if ($val_max ne 'U') { + print $field, ".value ", ($val_cur * 100 / $val_max), "\n"; + } +} +sub printValue { + if(not defined $_[0] || not defined $_[1]) { + exit 0; + } + my $field = $_[0]; + my $oid = $_[1]; + + if(not oidExists($oid)){ + return(0); + } + + my $val_cur = $session->get_single($oid) || 'U'; + if ($val_cur ne 'U') { + print $field, ".value ", $val_cur, "\n"; + } +} +sub oidExists { + if(not defined $_[0]) { + exit 0; + } + my $oid = $_[0]; + my $val = $session->get_single($oid); + + if(!length $val || $val eq 'noSuchInstance' || $val eq 'U'){ + return(0); + }else{ + return(1); + } +} diff --git a/plugins/printer/toshiba_5520c_byfunction_black_ b/plugins/printer/toshiba_5520c_byfunction_black_ index 7d8c8247..d20bb2ab 100755 --- a/plugins/printer/toshiba_5520c_byfunction_black_ +++ b/plugins/printer/toshiba_5520c_byfunction_black_ @@ -9,7 +9,7 @@ if [ "$1" = "config" ]; then echo "graph_title Toshiba 5520C: Black Pages (by function)" echo 'graph_vlabel Pages' echo 'graph_args --lower-limit 0' - echo 'graph_category printer' + echo 'graph_category printing' echo "blackPrintCopy.label Print: Copy" echo "blackPrintCopy.draw AREA" diff --git a/plugins/printer/toshiba_5520c_byfunction_fullcolor_ b/plugins/printer/toshiba_5520c_byfunction_fullcolor_ index b2cfa8f5..5a138be9 100755 --- a/plugins/printer/toshiba_5520c_byfunction_fullcolor_ +++ b/plugins/printer/toshiba_5520c_byfunction_fullcolor_ @@ -9,7 +9,7 @@ if [ "$1" = "config" ]; then echo "graph_title Toshiba 5520C: Full Color Pages (by function)" echo 'graph_vlabel Pages' echo 'graph_args --lower-limit 0' - echo 'graph_category printer' + echo 'graph_category printing' echo "fullColorPrintComputer.label Print from Computer" echo "fullColorPrintComputer.draw AREA" diff --git a/plugins/printer/toshiba_5520c_print_ b/plugins/printer/toshiba_5520c_print_ index 9814c5c0..c7d3d6b8 100755 --- a/plugins/printer/toshiba_5520c_print_ +++ b/plugins/printer/toshiba_5520c_print_ @@ -9,7 +9,7 @@ if [ "$1" = "config" ]; then echo "graph_title Toshiba 5520C: Pages Printed" echo 'graph_vlabel Pages' echo 'graph_args --lower-limit 0' - echo 'graph_category printer' + echo 'graph_category printing' echo "printBlack.label Black" echo "printBlack.draw AREA" diff --git a/plugins/printer/toshiba_5520c_scan_ b/plugins/printer/toshiba_5520c_scan_ index 829d0c55..c981c9d5 100755 --- a/plugins/printer/toshiba_5520c_scan_ +++ b/plugins/printer/toshiba_5520c_scan_ @@ -9,7 +9,7 @@ if [ "$1" = "config" ]; then echo "graph_title Toshiba 5520C: Pages Scanned" echo 'graph_vlabel Pages' echo 'graph_args --lower-limit 0' - echo 'graph_category printer' + echo 'graph_category printing' echo "scanFullColor.label Full Color" echo "scanFullColor.draw AREA" diff --git a/plugins/printer/xerox-wc3220 b/plugins/printer/xerox-wc3220 index 5645c1e7..dc695333 100755 --- a/plugins/printer/xerox-wc3220 +++ b/plugins/printer/xerox-wc3220 @@ -35,7 +35,7 @@ config) echo "graph_title Consumables level @ $PRINTER_IP" echo 'graph_vlabel %' -echo 'graph_category printer' +echo 'graph_category printing' echo 'graph_scale no' echo 'black.label Black toner level' echo 'black.draw LINE2' diff --git a/plugins/printer/xerox-wc7232-consumables b/plugins/printer/xerox-wc7232-consumables index 3f9417dc..d427da3f 100755 --- a/plugins/printer/xerox-wc7232-consumables +++ b/plugins/printer/xerox-wc7232-consumables @@ -35,7 +35,7 @@ config) echo "graph_title Consumables level @ $PRINTER_IP" echo 'graph_vlabel %' -echo 'graph_category printer' +echo 'graph_category printing' echo 'graph_scale no' echo 'cyan.label Cyan toner level' echo 'cyan.draw LINE2' diff --git a/plugins/processes/proc_mem_by_user b/plugins/processes/proc_mem_by_user deleted file mode 100644 index 5e763288..00000000 --- a/plugins/processes/proc_mem_by_user +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/sh -# -# (c) 2014, Gilles Fauvie -# Based on the 'du_multidirs' plugin, written by Christian Kujau -# -# Configure it by using the processes env var, i.e.: -# -# WARNING: SELINUX can block this plugin -# -# [proc_mem_by_user] -# env.users munin-node jprod -# - -. $MUNIN_LIBDIR/plugins/plugin.sh - -if [ "$1" = "autoconf" ]; then - echo yes - exit 0 -fi - -users=${users:="munin-node"} - -if [ "$1" = "config" ]; then - echo 'graph_title Memory usage by process by user' - echo 'graph_args --base 1024 -l 0' - echo 'graph_vlabel Bytes' - echo 'graph_category processes' - echo 'graph_info This graph shows the memory usage of several processes of one user' - - for user in $users; do - echo "$user.label $user" - done - -# echo "$u".warning 0 -# echo "$u".critical 0 - - exit 0 -fi - -for user in $users; do - echo "$user.value " `ps u -U $user | awk 'BEGIN { sum = 0 } NR > 1 { sum += $6 }; END { print sum * 1024 }'` -done \ No newline at end of file diff --git a/plugins/prosody/prosody_ b/plugins/prosody/prosody_ old mode 100644 new mode 100755 index 17d3da29..4cab0d6a --- a/plugins/prosody/prosody_ +++ b/plugins/prosody/prosody_ @@ -25,6 +25,7 @@ import os import telnetlib import re + def main(): try: mode = sys.argv[1] @@ -35,56 +36,50 @@ def main(): port = int(os.environ.get('port', 5582)) if mode == "suggest": - print "c2s" - print "s2s" - print "presence" - print "uptime" - print "users" + print("c2s") + print("s2s") + print("presence") + print("uptime") + print("users") sys.exit(0) if wildcard == "c2s": if mode == "config": - print "graph_title Prosody C2S Connections" - print "graph_vlabel users" - print "graph_category Prosody" + print("graph_title Prosody C2S Connections") + print("graph_vlabel users") + print("graph_category chat") - print "all_client_connections.label client connections" - print "secure_client_connections.label secure client connections" - print "insecure_client_connections.label insecure client " \ - "connections" + print("all_client_connections.label client connections") + print("secure_client_connections.label secure client connections") + print("insecure_client_connections.label insecure client connections") sys.exit(0) else: connection_count_re = re.compile(r"Total:\s(\d+)\s") telnet = telnetlib.Telnet(host, port) telnet.write("c2s:show_secure()\n") - telnet_response = telnet.read_until("secure client connections", - 5) + telnet_response = telnet.read_until("secure client connections", 5) parsed_info = connection_count_re.findall(telnet_response) secure_client_connections = int(parsed_info[0]) - print "secure_client_connections.value %s" % \ - (secure_client_connections) + print("secure_client_connections.value %s" % secure_client_connections) telnet.write("c2s:show_insecure()\n") - telnet_response = telnet.read_until("insecure client connections", - 5) + telnet_response = telnet.read_until("insecure client connections", 5) parsed_info = connection_count_re.findall(telnet_response) insecure_client_connections = int(parsed_info[0]) - print "insecure_client_connections.value %s" % \ - (insecure_client_connections) - all_client_connections = secure_client_connections + \ - insecure_client_connections - print "all_client_connections.value %s" % (all_client_connections) + print("insecure_client_connections.value %s" % insecure_client_connections) + all_client_connections = secure_client_connections + insecure_client_connections + print("all_client_connections.value %s" % (all_client_connections)) telnet.write("quit\n") elif wildcard == "s2s": if mode == "config": - print "graph_title Prosody S2S Connections" - print "graph_vlabel servers" - print "graph_category Prosody" + print("graph_title Prosody S2S Connections") + print("graph_vlabel servers") + print("graph_category chat") - print "outgoing_connections.label outgoing connections" - print "incoming_connections.label incoming connections" + print("outgoing_connections.label outgoing connections") + print("incoming_connections.label incoming connections") sys.exit(0) else: @@ -93,21 +88,21 @@ def main(): telnet.write("s2s:show()\n") telnet_response = telnet.read_until("connections", 5) parsed_info = server_connections_re.findall(telnet_response) - print "outgoing_connections.value %s" % (parsed_info[0][0]) - print "incoming_connections.value %s" % (parsed_info[0][1]) + print("outgoing_connections.value %s" % (parsed_info[0][0])) + print("incoming_connections.value %s" % (parsed_info[0][1])) telnet.write("quit\n") elif wildcard == "presence": if mode == "config": - print "graph_title Prosody Client Presence" - print "graph_vlabel clients" - print "graph_category Prosody" + print("graph_title Prosody Client Presence") + print("graph_vlabel clients") + print("graph_category chat") - print "available.label Avaible Clients" - print "chat.label Ready for Chat Clients" - print "away.label Away Clients" - print "xa.label Extended Away Clients" - print "dnd.label Do Not Disturb Clients" + print("available.label Avaible Clients") + print("chat.label Ready for Chat Clients") + print("away.label Away Clients") + print("xa.label Extended Away Clients") + print("dnd.label Do Not Disturb Clients") sys.exit(0) else: @@ -116,26 +111,26 @@ def main(): telnet.write("c2s:show()\n") telnet_response = telnet.read_until("clients", 5) parsed_info = client_presence_re.findall(telnet_response) - print "available.value %s" % (parsed_info.count("available")) - print "chat.value %s" % (parsed_info.count("chat")) - print "away.value %s" % (parsed_info.count("away")) - print "xa.value %s" % (parsed_info.count("xa")) - print "dnd.value %s" % (parsed_info.count("dnd")) + print("available.value %s" % parsed_info.count("available")) + print("chat.value %s" % (parsed_info.count("chat"))) + print("away.value %s" % (parsed_info.count("away"))) + print("xa.value %s" % (parsed_info.count("xa"))) + print("dnd.value %s" % (parsed_info.count("dnd"))) telnet.write("quit\n") elif wildcard == "uptime": if mode == "config": - print "graph_title Prosody Uptime" - print "graph_args --base 1000 -l 0" - print "graph_scale no" - print "graph_vlabel uptime in days" - print "graph_category Prosody" - print "graph_order uptime" - print "uptime.draw AREA" - print "uptime.min U" - print "uptime.max U" - print "uptime.label uptime" - print "uptime.type GAUGE" + print("graph_title Prosody Uptime") + print("graph_args --base 1000 -l 0") + print("graph_scale no") + print("graph_vlabel uptime in days") + print("graph_category chat") + print("graph_order uptime") + print("uptime.draw AREA") + print("uptime.min U") + print("uptime.max U") + print("uptime.label uptime") + print("uptime.type GAUGE") sys.exit(0) else: @@ -144,16 +139,16 @@ def main(): telnet.write("server:uptime()\n") telnet_response = telnet.read_until("minutes (", 5) parsed_info = uptime_re.findall(telnet_response) - uptime_value = float(parsed_info[0]) + float(parsed_info[1])/24 +\ - float(parsed_info[2])/60/24 - print "uptime.value %s" % (uptime_value) + uptime_value = (float(parsed_info[0]) + float(parsed_info[1])/24 + + float(parsed_info[2])/60/24) + print("uptime.value %s" % (uptime_value)) telnet.write("quit\n") elif wildcard == "users": if mode == "config": - print "graph_title Prosody Registered Users" - print "graph_vlabel users" - print "graph_category Prosody" + print("graph_title Prosody Registered Users") + print("graph_vlabel users") + print("graph_category chat") base_dir = os.environ.get('internal_storage_path', "/var/lib/prosody") if os.path.isdir(base_dir): @@ -161,23 +156,125 @@ def main(): for vhost in vhosts: account_dir = os.path.join(base_dir, vhost, "accounts") if os.path.isdir(account_dir): - vhost = vhost.replace("%2e",".") - munin_var = vhost.replace(".","_") + vhost = vhost.replace("%2e", ".") + munin_var = vhost.replace(".", "_") if mode == "config": - print "%s.label %s" % (munin_var, vhost) + print("%s.label %s" % (munin_var, vhost)) else: accounts = len(list(listfiles(account_dir))) - print "%s.value %s" % (munin_var, accounts) + print("%s.value %s" % (munin_var, accounts)) + def listdirs(folder): for x in os.listdir(folder): if os.path.isdir(os.path.join(folder, x)): yield x + def listfiles(folder): for x in os.listdir(folder): if os.path.isfile(os.path.join(folder, x)): yield x + if __name__ == '__main__': main() + + +# Here starts the prosody_ plugin documentation, intended to be used with munindoc and in +# plugin gallery. +""" +=head1 NAME + +prosody_ - Munin wildcard-plugin to monitor a L xmpp server. + +This wildcard plugin provides at the moment only the suffixes C, C, C, +C and C suffixes. + +=head1 INSTALLATION + +It is very simple to install the plugin. + +=over 2 + + cd /usr/share/munin/plugins (or your munin plugins directory) + wget https://github.com/jarus/munin-prosody/raw/master/prosody_ + chmod 755 prosody_ + + ln -s /usr/share/munin/plugins/prosody_ /etc/munin/plugins/prosody_c2s + ln -s /usr/share/munin/plugins/prosody_ /etc/munin/plugins/prosody_s2s + ln -s /usr/share/munin/plugins/prosody_ /etc/munin/plugins/prosody_presence + ln -s /usr/share/munin/plugins/prosody_ /etc/munin/plugins/prosody_uptime + ln -s /usr/share/munin/plugins/prosody_ /etc/munin/plugins/prosody_users + +=back + +After the installation you need to restart your munin-node: + +=over 2 + + service munin-node restart + +=back + +=head1 CONFIGURATION + +When you want to change the default host (localhost) and port (5582) do it in a file named prosody +placed in the directory /etc/munin/plugin-conf.d/ with a config like this: + +=over 2 + + [prosody_*] + env.host example.com + env.port 5582 + +=back + +If you want to get the number of registered users, add the following lines to +/etc/munin/plugin-conf.d/prosody: + +=over 2 + + [prosody_users] + user prosody + group prosody + +=back + +=head1 VERSION + +Version 2.2 + +=head1 BUGS + +None known + +=head1 AUTHOR + +(C) 2010 Christoph Heer + +=head1 LICENSE + +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. + + +=cut + + +""" diff --git a/plugins/puma/puma_ b/plugins/puma/puma_ new file mode 100755 index 00000000..85db9d09 --- /dev/null +++ b/plugins/puma/puma_ @@ -0,0 +1,60 @@ +#!/bin/sh +# Copyright (c) 2016 Alexey Anikanov (alexey.anikanov@gmail.com) +# License GPLv2 +# This plugin monitors number of workers, total memory used and average memory per process for puma, +# a ruby web server built for concurrency http://puma.io +# Here are the symlinks to enable it +# +# ln -s /usr/share/munin/plugins/puma_ /etc/munin/plugins/puma_average +# ln -s /usr/share/munin/plugins/puma_ /etc/munin/plugins/puma_memory +# ln -s /usr/share/munin/plugins/puma_ /etc/munin/plugins/puma_processes + +mode=$(echo $0 | cut -d _ -f 2) + +if [ "$1" = "suggest" ]; then + echo "memory" + echo "processes" + echo "average" + exit 0 +fi + +if [ "$mode" = "memory" ]; then + if [ "$1" = "config" ]; then + echo "graph_title Total Puma Memory" + echo "graph_vlabel Total RAM" + echo "graph_category webserver" + echo "graph_args --base 1024" + echo "ram.label Total RAM" + exit 0 + else + processes_memory="$(ps auwx | egrep "puma.* worker" | grep -v grep | awk '{print $6 }')" + for process_memory in $processes_memory; do + total_memory=$(( $total_memory + ( $process_memory * 1024) )) + done + printf "ram.value %s\n" "$total_memory" + fi +elif [ "$mode" = "processes" ]; then + if [ "$1" = "config" ]; then + echo "graph_title puma Processes" + echo "graph_vlabel Processes" + echo "graph_category webserver" + echo "processes.label active processes" + else + printf "processes.value %s\n" "$(ps awwwux | egrep "puma.* worker" | grep -v grep | wc -l)" + exit 0 + fi +elif [ "$mode" = "average" ]; then + if [ "$1" = "config" ]; then + echo 'graph_title Puma Average Process Size' + echo 'graph_args --base 1024 -l 0 ' + echo 'graph_vlabel Average Process Size' + echo 'graph_category webserver' + echo 'puma_average.label Average Process Size' + echo 'puma_average.draw LINE2' + echo 'puma_average.info The average process size for puma' + else + printf "puma_average.value %s\n" "$(ps awwwux | egrep "puma.* worker" | grep -v grep | grep -v master | awk '{total_mem = $6 * 1024 + total_mem; total_proc++} END{printf("%d\n", total_mem / total_proc)}')" + exit 0 + fi +fi +exit 0 diff --git a/plugins/forums/punbb_users b/plugins/punbb/punbb_users similarity index 100% rename from plugins/forums/punbb_users rename to plugins/punbb/punbb_users diff --git a/plugins/puppet/puppet_runtime b/plugins/puppet/puppet_runtime index c4eea5ca..8962f4df 100755 --- a/plugins/puppet/puppet_runtime +++ b/plugins/puppet/puppet_runtime @@ -29,12 +29,12 @@ end case ARGV[0] when 'config' - puts "graph_category puppet" - puts "graph_args --base 1000 -l 0" - puts "graph_scale no" - puts "graph_title puppet catalog run time" - puts "graph_vlabel Seconds" - puts "runtime.label Catalog application time" + puts "graph_category devel" + puts "graph_args --base 1000 -l 0" + puts "graph_scale no" + puts "graph_title puppet catalog run time" + puts "graph_vlabel Seconds" + puts "runtime.label Catalog application time" exit 0 when 'autoconf' puts "yes" diff --git a/plugins/mail/qmailconn b/plugins/qmail/qmailconn similarity index 100% rename from plugins/mail/qmailconn rename to plugins/qmail/qmailconn diff --git a/plugins/mail/qmailsend b/plugins/qmail/qmailsend similarity index 100% rename from plugins/mail/qmailsend rename to plugins/qmail/qmailsend diff --git a/plugins/mail/qmailsend_plesk b/plugins/qmail/qmailsend_plesk similarity index 100% rename from plugins/mail/qmailsend_plesk rename to plugins/qmail/qmailsend_plesk diff --git a/plugins/mail/qremote b/plugins/qmail/qremote similarity index 100% rename from plugins/mail/qremote rename to plugins/qmail/qremote diff --git a/plugins/mail/queuestats b/plugins/qmail/queuestats similarity index 96% rename from plugins/mail/queuestats rename to plugins/qmail/queuestats index 4385f68e..afeb683e 100755 --- a/plugins/mail/queuestats +++ b/plugins/qmail/queuestats @@ -19,7 +19,7 @@ if [ "$1" = "config" ]; then graph_title Qmail queue stats graph_args --base 1000 -l 0 graph_vlabel mails in queue -graph_category Mail +graph_category mail queue.label queue queue.type GAUGE queue.min 0 diff --git a/plugins/mail/spamdyke b/plugins/qmail/spamdyke similarity index 99% rename from plugins/mail/spamdyke rename to plugins/qmail/spamdyke index fd2dbb34..619ebd24 100755 --- a/plugins/mail/spamdyke +++ b/plugins/qmail/spamdyke @@ -29,7 +29,7 @@ mktemp -t MAIL_LOG=${logfile:-/var/log/mail.info} LOGTAIL=${logtail:-`which logtail`} -STATEFILE=/var/lib/munin/plugin-state/spamdyke.offset +STATEFILE=$MUNIN_PLUGSTATE/spamdyke.offset if [ "$1" = "autoconf" ]; then if [ -f "${MAIL_LOG}" -a -n "${LOGTAIL}" -a -x "${LOGTAIL}" ] ; then diff --git a/plugins/qpsmtpd/qpsmtpd_mailstats b/plugins/qpsmtpd/qpsmtpd_mailstats index 0077b917..dd97d288 100755 --- a/plugins/qpsmtpd/qpsmtpd_mailstats +++ b/plugins/qpsmtpd/qpsmtpd_mailstats @@ -96,7 +96,7 @@ sub do_config { my $k; print "graph_title QPSMTPD Responses -graph_category QPSMTPD +graph_category mail graph_args --base 1000 -l 0 graph_vlabel responses / \${graph_period} graph_scale no diff --git a/plugins/games/qstat b/plugins/quake/qstat similarity index 78% rename from plugins/games/qstat rename to plugins/quake/qstat index 7438b2a8..d0657b0d 100755 --- a/plugins/games/qstat +++ b/plugins/quake/qstat @@ -13,7 +13,7 @@ qstat_exe='/usr/local/bin/qstat' #---------------------------------------------------------------# # End of config -script_name=$(basename $0) +script_name=$(basename "$0") ################################################################# ################################################################# @@ -29,9 +29,9 @@ usage() { config() { if [ "${script_name}" != "qstat_" ]; then - gametype=$(echo ${script_name} | cut -d_ -f2) - ip=$(echo ${script_name} | cut -d_ -f3) - port=$(echo ${script_name} | cut -d_ -f4) + gametype=$(echo "$script_name" | cut -d_ -f2) + ip=$(echo "$script_name" | cut -d_ -f3) + port=$(echo "$script_name" | cut -d_ -f4) else gametype=$1 ip=$2 @@ -51,20 +51,20 @@ player.label players" #---------------------------------------------------------------# quake_stat() { if [ "${script_name}" != "qstat_" ]; then - gametype=$(echo ${script_name} | cut -d_ -f2) - ip=$(echo ${script_name} | cut -d_ -f3) - port=$(echo ${script_name} | cut -d_ -f4) + gametype=$(echo "$script_name" | cut -d_ -f2) + ip=$(echo "$script_name" | cut -d_ -f3) + port=$(echo "$script_name" | cut -d_ -f4) else gametype=$1 ip=$2 port=$3 fi - if [ ! -z ${gametype} ] && [ ! -z ${gametype} ] && [ ! -z ${gametype} ]; then - dummy=$(${qstat_exe} -raw ";" -nh -${gametype} ${ip}:${port}) + if [ ! -z "$gametype" ] && [ ! -z "$gametype" ] && [ ! -z "$gametype" ]; then + dummy=$("$qstat_exe" -raw ";" -nh "-$gametype" "${ip}:${port}") - playervalue=$(echo ${dummy} | cut -d\; -f6) - maxplayervalue=$(echo ${dummy} | cut -d\; -f5) + playervalue=$(echo "$dummy" | cut -d\; -f6) + maxplayervalue=$(echo "$dummy" | cut -d\; -f5) if [ -z "${playervalue}" ]; then playervalue=0 @@ -88,7 +88,7 @@ quake_stat() { #---------------------------------------------------------------# case $1 in config) - config + config "$@" exit 0 ;; help | ?) @@ -99,7 +99,7 @@ case $1 in echo "no (edit the script for set qstat path)" ;; *) - quake_stat $1 $2 $3 + quake_stat "$@" exit 0 ;; esac diff --git a/plugins/games/qstatet_ b/plugins/quake/qstatet_ similarity index 77% rename from plugins/games/qstatet_ rename to plugins/quake/qstatet_ index 105d4b67..d792f235 100755 --- a/plugins/games/qstatet_ +++ b/plugins/quake/qstatet_ @@ -13,7 +13,7 @@ qstat_exe='/usr/bin/qstat' #---------------------------------------------------------------# # End of config -script_name=$(basename $0) +script_name=$(basename "$0") ################################################################# ################################################################# @@ -29,9 +29,9 @@ usage() { config() { if [ "${script_name}" != "qstat_" ]; then - gametype=$(echo ${script_name} | cut -d_ -f2) - ip=$(echo ${script_name} | cut -d_ -f3) - port=$(echo ${script_name} | cut -d_ -f4) + gametype=$(echo "$script_name" | cut -d_ -f2) + ip=$(echo "$script_name" | cut -d_ -f3) + port=$(echo "$script_name" | cut -d_ -f4) else gametype=$1 ip=$2 @@ -51,20 +51,19 @@ player.label players" #---------------------------------------------------------------# quake_stat() { if [ "${script_name}" != "qstat_" ]; then - gametype=$(echo ${script_name} | cut -d_ -f2) - ip=$(echo ${script_name} | cut -d_ -f3) - port=$(echo ${script_name} | cut -d_ -f4) + gametype=$(echo "$script_name" | cut -d_ -f2) + ip=$(echo "$script_name" | cut -d_ -f3) + port=$(echo "$script_name" | cut -d_ -f4) else gametype=$1 ip=$2 port=$3 fi - if [ ! -z ${gametype} ] && [ ! -z ${gametype} ] && [ ! -z ${gametype} ]; then - dummy=$(${qstat_exe} -P -pa -sort P -${gametype} ${ip}:${port} | grep frags | grep -wv 0ms | wc -l) - dummy2=$(${qstat_exe} -P -pa -sort P -${gametype} ${ip}:${port} | grep frags | grep -w 0ms | wc -l) - playervalue=$dummy - maxplayervalue=$dummy2 + if [ ! -z "$gametype" ] && [ ! -z "$gametype" ] && [ ! -z "$gametype" ]; then + stat_output=$("$qstat_exe" -P -pa -sort P "-$gametype" "${ip}:${port}" | grep frags) + playervalue=$(echo "$stat_output" | grep -cwv 0ms) + maxplayervalue=$(echo "$stat_output" | grep -cw 0ms) if [ -z "${playervalue}" ]; then playervalue=0 @@ -88,7 +87,7 @@ quake_stat() { #---------------------------------------------------------------# case $1 in config) - config + config "$@" exit 0 ;; help | ?) @@ -99,7 +98,7 @@ case $1 in echo "no (edit the script for set qstat path)" ;; *) - quake_stat $1 $2 $3 + quake_stat "$@" exit 0 ;; esac diff --git a/plugins/games/qstatqw_ b/plugins/quake/qstatqw_ similarity index 77% rename from plugins/games/qstatqw_ rename to plugins/quake/qstatqw_ index 1538b87b..df29156d 100755 --- a/plugins/games/qstatqw_ +++ b/plugins/quake/qstatqw_ @@ -13,7 +13,7 @@ qstat_exe='/usr/bin/qstat' #---------------------------------------------------------------# # End of config -script_name=$(basename $0) +script_name=$(basename "$0") ################################################################# ################################################################# @@ -29,9 +29,9 @@ usage() { config() { if [ "${script_name}" != "qstat_" ]; then - gametype=$(echo ${script_name} | cut -d_ -f2) - ip=$(echo ${script_name} | cut -d_ -f3) - port=$(echo ${script_name} | cut -d_ -f4) + gametype=$(echo "$script_name" | cut -d_ -f2) + ip=$(echo "$script_name" | cut -d_ -f3) + port=$(echo "$script_name" | cut -d_ -f4) else gametype=$1 ip=$2 @@ -51,20 +51,19 @@ player.label players" #---------------------------------------------------------------# quake_stat() { if [ "${script_name}" != "qstat_" ]; then - gametype=$(echo ${script_name} | cut -d_ -f2) - ip=$(echo ${script_name} | cut -d_ -f3) - port=$(echo ${script_name} | cut -d_ -f4) + gametype=$(echo "$script_name" | cut -d_ -f2) + ip=$(echo "$script_name" | cut -d_ -f3) + port=$(echo "$script_name" | cut -d_ -f4) else gametype=$1 ip=$2 port=$3 fi - if [ ! -z ${gametype} ] && [ ! -z ${gametype} ] && [ ! -z ${gametype} ]; then - dummy=$(${qstat_exe} -P -pa -sort P -${gametype} ${ip}:${port} | grep team | grep -wv 0ms | wc -l) - dummy2=$(${qstat_exe} -P -pa -sort P -${gametype} ${ip}:${port} | grep team | grep -w 0ms | wc -l) - playervalue=$dummy - maxplayervalue=$dummy2 + if [ ! -z "$gametype" ] && [ ! -z "$gametype" ] && [ ! -z "$gametype" ]; then + stat_output=$("$qstat_exe" -P -pa -sort P "-${gametype}" "${ip}:${port}" | grep team) + playervalue=$(echo "$stat_output" | grep -cwv 0ms) + maxplayervalue=$(echo "$stat_output" | grep -cw 0ms) if [ -z "${playervalue}" ]; then playervalue=0 @@ -74,7 +73,6 @@ quake_stat() { maxplayervalue=0 fi - echo "maxplayer.value "${maxplayervalue}; echo "player.value "${playervalue}; else @@ -88,7 +86,7 @@ quake_stat() { #---------------------------------------------------------------# case $1 in config) - config + config "$@" exit 0 ;; help | ?) @@ -99,8 +97,7 @@ case $1 in echo "no (edit the script for set qstat path)" ;; *) - quake_stat $1 $2 $3 + quake_stat "$@" exit 0 ;; esac - diff --git a/plugins/rabbitmq/rabbitmq_connections b/plugins/rabbitmq/rabbitmq_connections index b9eee25a..f7adbc5a 100755 --- a/plugins/rabbitmq/rabbitmq_connections +++ b/plugins/rabbitmq/rabbitmq_connections @@ -72,7 +72,7 @@ if [ "$1" = "config" ]; then # We want Cur/Min/Avg/Max unscaled (i.e. 0.42 load instead of # 420 milliload) #echo 'graph_scale no' - echo 'graph_category RabbitMQ' + echo 'graph_category chat' echo "connections.label Connections" echo "connections.warning $CONN_WARN" @@ -89,7 +89,7 @@ fi # real work - i.e. display the data. Almost always this will be # "value" subfield for every data field. -if hash rabbitmqctl >/dev/null 2>&1; then +if command -v rabbitmqctl >/dev/null 2>&1; then connections=$(HOME=/tmp rabbitmqctl list_connections state | grep -c running) else echo "$0: Could not run rabbitmqctl" >&2 diff --git a/plugins/rabbitmq/rabbitmq_consumers b/plugins/rabbitmq/rabbitmq_consumers index bb46c493..76ce6759 100755 --- a/plugins/rabbitmq/rabbitmq_consumers +++ b/plugins/rabbitmq/rabbitmq_consumers @@ -16,7 +16,7 @@ #%# capabilities=autoconf # If run with the "autoconf"-parameter, give our opinion on whether we -# should be run on this system or not. This is optinal, and only used by +# should be run on this system or not. This is optional, and only used by # munin-config. In the case of this plugin, we should most probably # always be included. @@ -51,7 +51,7 @@ if [ "$1" = "config" ]; then # We want Cur/Min/Avg/Max unscaled (i.e. 0.42 load instead of # 420 milliload) #echo 'graph_scale no' - echo 'graph_category RabbitMQ' + echo 'graph_category chat' for queue in $QUEUES; do echo "$queue.label $queue" diff --git a/plugins/rabbitmq/rabbitmq_messages b/plugins/rabbitmq/rabbitmq_messages index 92595ed3..a1b8f688 100755 --- a/plugins/rabbitmq/rabbitmq_messages +++ b/plugins/rabbitmq/rabbitmq_messages @@ -16,7 +16,7 @@ #%# capabilities=autoconf # If run with the "autoconf"-parameter, give our opinion on whether we -# should be run on this system or not. This is optinal, and only used by +# should be run on this system or not. This is optional, and only used by # munin-config. In the case of this plugin, we should most probably # always be included. @@ -51,7 +51,7 @@ if [ "$1" = "config" ]; then # We want Cur/Min/Avg/Max unscaled (i.e. 0.42 load instead of # 420 milliload) #echo 'graph_scale no' - echo 'graph_category RabbitMQ' + echo 'graph_category chat' for queue in $QUEUES; do echo "$queue.label $queue" diff --git a/plugins/rabbitmq/rabbitmq_messages_unacknowledged b/plugins/rabbitmq/rabbitmq_messages_unacknowledged index a1782d92..d5d8f60f 100755 --- a/plugins/rabbitmq/rabbitmq_messages_unacknowledged +++ b/plugins/rabbitmq/rabbitmq_messages_unacknowledged @@ -16,7 +16,7 @@ #%# capabilities=autoconf # If run with the "autoconf"-parameter, give our opinion on whether we -# should be run on this system or not. This is optinal, and only used by +# should be run on this system or not. This is optional, and only used by # munin-config. In the case of this plugin, we should most probably # always be included. @@ -51,7 +51,7 @@ if [ "$1" = "config" ]; then # We want Cur/Min/Avg/Max unscaled (i.e. 0.42 load instead of # 420 milliload) #echo 'graph_scale no' - echo 'graph_category RabbitMQ' + echo 'graph_category chat' for queue in $QUEUES; do echo "$queue.label $queue" diff --git a/plugins/rabbitmq/rabbitmq_messages_uncommitted b/plugins/rabbitmq/rabbitmq_messages_uncommitted index aed85513..179d2e60 100755 --- a/plugins/rabbitmq/rabbitmq_messages_uncommitted +++ b/plugins/rabbitmq/rabbitmq_messages_uncommitted @@ -16,7 +16,7 @@ #%# capabilities=autoconf # If run with the "autoconf"-parameter, give our opinion on whether we -# should be run on this system or not. This is optinal, and only used by +# should be run on this system or not. This is optional, and only used by # munin-config. In the case of this plugin, we should most probably # always be included. @@ -51,7 +51,7 @@ if [ "$1" = "config" ]; then # We want Cur/Min/Avg/Max unscaled (i.e. 0.42 load instead of # 420 milliload) #echo 'graph_scale no' - echo 'graph_category RabbitMQ' + echo 'graph_category chat' for queue in $QUEUES; do echo "$queue.label $queue" diff --git a/plugins/rabbitmq/rabbitmq_queue_memory b/plugins/rabbitmq/rabbitmq_queue_memory index a6c752a7..fef2cc7b 100755 --- a/plugins/rabbitmq/rabbitmq_queue_memory +++ b/plugins/rabbitmq/rabbitmq_queue_memory @@ -16,7 +16,7 @@ #%# capabilities=autoconf # If run with the "autoconf"-parameter, give our opinion on whether we -# should be run on this system or not. This is optinal, and only used by +# should be run on this system or not. This is optional, and only used by # munin-config. In the case of this plugin, we should most probably # always be included. @@ -51,7 +51,7 @@ if [ "$1" = "config" ]; then # We want Cur/Min/Avg/Max unscaled (i.e. 0.42 load instead of # 420 milliload) #echo 'graph_scale no' - echo 'graph_category RabbitMQ' + echo 'graph_category chat' for queue in $QUEUES; do echo "$queue.label $queue" diff --git a/plugins/rackspace/rackspace_cdn_count.php b/plugins/rackspace/rackspace_cdn_count.php old mode 100644 new mode 100755 index 71356e2d..d1ee8cbb --- a/plugins/rackspace/rackspace_cdn_count.php +++ b/plugins/rackspace/rackspace_cdn_count.php @@ -25,7 +25,7 @@ function SplitTwice($content,$first,$second) { if ($argv[1]=='config'){ print "graph_title Rackspace CDN files count\n"; print "graph_vlabel Files Count\n"; - print "graph_category rackspace\n"; + print "graph_category cloud\n"; print "count.label files count\n"; print "graph_args --base 1000\n"; diff --git a/plugins/rackspace/rackspace_cdn_size.php b/plugins/rackspace/rackspace_cdn_size.php old mode 100644 new mode 100755 index 45fca542..b3b836fd --- a/plugins/rackspace/rackspace_cdn_size.php +++ b/plugins/rackspace/rackspace_cdn_size.php @@ -24,7 +24,7 @@ function SplitTwice($content,$first,$second) { if ($argv[1]=='config'){ print "graph_title Rackspace CDN storage usage\n"; print "graph_vlabel CDN storage usage\n"; - print "graph_category rackspace\n"; + print "graph_category cloud\n"; print "usage.label storage usage\n"; print "graph_args --base 1024\n"; diff --git a/plugins/radiator/radiator_acct_lag b/plugins/radiator/radiator_acct_lag index 7c0010f2..be9ab4e8 100755 --- a/plugins/radiator/radiator_acct_lag +++ b/plugins/radiator/radiator_acct_lag @@ -35,7 +35,7 @@ if [ "$1" = "config" ]; then echo 'graph_title Radiator ACCT response lag' echo 'graph_vlabel time' echo 'graph_args -l 0' - echo 'graph_category radiator' + echo 'graph_category auth' echo 'graph_info This graph displayes ACCT response lag of all radiator processes combined.' # # Count all statistics files and create labels diff --git a/plugins/radiator/radiator_acct_ppm b/plugins/radiator/radiator_acct_ppm index 22c2a139..04ef8687 100755 --- a/plugins/radiator/radiator_acct_ppm +++ b/plugins/radiator/radiator_acct_ppm @@ -36,7 +36,7 @@ if [ "$1" = "config" ]; then echo 'graph_period minute' echo 'graph_vlabel packets per minute' echo 'graph_args -l 0' - echo 'graph_category radiator' + echo 'graph_category auth' echo 'graph_info This graph displays ACCT packets of all radiator processes combined.' # # Count all statistics files and create labels diff --git a/plugins/radiator/radiator_auth_lag b/plugins/radiator/radiator_auth_lag index 0a59042e..9b33b0d1 100755 --- a/plugins/radiator/radiator_auth_lag +++ b/plugins/radiator/radiator_auth_lag @@ -35,7 +35,7 @@ if [ "$1" = "config" ]; then echo 'graph_title Radiator AUTH response lag' echo 'graph_vlabel time' echo 'graph_args -l 0' - echo 'graph_category radiator' + echo 'graph_category auth' echo 'graph_info This graph displayes AUTH response lag of all radiator processes combined.' # # Count all statistics files and create labels diff --git a/plugins/radiator/radiator_auth_ppm b/plugins/radiator/radiator_auth_ppm index 70111bd2..0e4c5d14 100755 --- a/plugins/radiator/radiator_auth_ppm +++ b/plugins/radiator/radiator_auth_ppm @@ -36,7 +36,7 @@ if [ "$1" = "config" ]; then echo 'graph_period minute' echo 'graph_vlabel packets per minute' echo 'graph_args -l 0' - echo 'graph_category radiator' + echo 'graph_category auth' echo 'graph_info This graph displayes AUTH packets of all radiator processes combined.' # # Count all statistics files and create labels diff --git a/plugins/raspberry-pi/cpu_freq_1sec b/plugins/raspberry-pi/cpu_freq_1sec index 44e01214..62a26523 100755 --- a/plugins/raspberry-pi/cpu_freq_1sec +++ b/plugins/raspberry-pi/cpu_freq_1sec @@ -40,7 +40,7 @@ if [ "$1" = "config" ] then cat < -pluginfull="$0" # full name of plugin -plugin="${0##*/}" # name of plugin -pidfile="$MUNIN_PLUGSTATE/munin.$plugin.pid" -cache="$MUNIN_PLUGSTATE/munin.$plugin.value" - if [ ! -r "/sys/class/thermal/thermal_zone0/temp" ] then @@ -28,6 +23,6 @@ fi # values TEMP_MILLI_C=$(cat /sys/class/thermal/thermal_zone0/temp) -echo thermal_zone0.value $( echo "scale=3; $TEMP_MILLI_C / 1000" | bc ) +echo "thermal_zone0.value $(echo "$TEMP_MILLI_C" | awk '{printf("%0.3f", $1 / 1000)}')" exit 0 diff --git a/plugins/reddit_karma/reddit_karma_ b/plugins/reddit_karma/reddit_karma_ index 855cd478..689a283c 100755 --- a/plugins/reddit_karma/reddit_karma_ +++ b/plugins/reddit_karma/reddit_karma_ @@ -32,7 +32,7 @@ reddit_user=${0##*reddit_karma_} ## if [ "$1" = "autoconf" ]; then # Check that curl is installed - if hash curl >/dev/null 2>&1; then + if command -v curl >/dev/null 2>&1; then echo "yes" else echo "no (no curl installed)" @@ -49,7 +49,7 @@ if [ "$1" = "config" ]; then echo 'graph_args --base 1000' echo 'graph_scale no' echo 'graph_vlabel Link Karma' - echo 'graph_category Reddit' + echo 'graph_category other' echo 'comment_karma.label Comment Karma' echo 'comment_karma.draw LINE' echo 'link_karma.label Link Karma' @@ -61,8 +61,9 @@ fi # Main ## # Get current karma stats. -link_karma=$(curl -s http://www.reddit.com/user/${reddit_user}/about.json | grep -Eo 'link_karma": [0-9]+' | cut -d' ' -f2) -comment_karma=$(curl -s http://www.reddit.com/user/${reddit_user}/about.json | grep -Eo 'comment_karma": [0-9]+' | cut -d' ' -f2) +about_user_url="http://www.reddit.com/user/${reddit_user}/about.json" +link_karma=$(curl -s "$about_user_url" | grep -Eo 'link_karma": [0-9]+' | cut -d' ' -f2) +comment_karma=$(curl -s "$about_user_url" | grep -Eo 'comment_karma": [0-9]+' | cut -d' ' -f2) # Output karma stats. echo "link_karma.value $link_karma" diff --git a/plugins/redis/redis-speed b/plugins/redis/redis-speed index 9864e6b6..fd190da3 100755 --- a/plugins/redis/redis-speed +++ b/plugins/redis/redis-speed @@ -50,7 +50,7 @@ if ( $config ) { print "ping.label PING time\n"; print "get.label GET time\n"; print "set.label SET time\n"; - print "graph_category redis\n"; + print "graph_category search\n"; exit 0; } diff --git a/plugins/redis/redis_ b/plugins/redis/redis_ index 2d61ca9d..55474435 100755 --- a/plugins/redis/redis_ +++ b/plugins/redis/redis_ @@ -80,7 +80,7 @@ switch ($0) { my $maxclients= get_config("maxclients")->{"maxclients"}; print "graph_title ${TITLE_PREFIX}Connected clients\n"; print "graph_vlabel Connected clients\n"; - print "graph_category redis\n"; + print "graph_category search\n"; print "graph_args -l 0\n"; print "connected_clients.line $maxclients:ff0000:Limit\n"; print "connected_clients.label connected clients\n"; @@ -95,7 +95,7 @@ switch ($0) { if ( $config ) { print "graph_title ${TITLE_PREFIX}Keys Per Second\n"; print "graph_vlabel per \${graph_period}\n"; - print "graph_category redis\n"; + print "graph_category search\n"; print "graph_args -l 0\n"; print "hits.label hits\n"; print "hits.type COUNTER\n"; @@ -118,7 +118,7 @@ switch ($0) { if ( $config ) { print "graph_title ${TITLE_PREFIX}Key Hit vs Miss Ratio\n"; print "graph_vlabel per \${graph_period}\n"; - print "graph_category redis\n"; + print "graph_category search\n"; print "graph_args -u 100 -l 0 -r --base 1000\n"; print "hitratio.label hit ratio\n"; print "hitratio.type GAUGE\n"; @@ -145,7 +145,7 @@ switch ($0) { if ( $config ) { print "graph_title ${TITLE_PREFIX}Per second\n"; print "graph_vlabel per \${graph_period}\n"; - print "graph_category redis\n"; + print "graph_category search\n"; print "graph_args -l 0\n"; print "requests.label requests\n"; print "requests.type COUNTER\n"; @@ -164,7 +164,7 @@ switch ($0) { my $maxmemory = get_config("maxmemory")->{"maxmemory"}; print "graph_title ${TITLE_PREFIX}Used memory\n"; print "graph_vlabel Used memory\n"; - print "graph_category redis\n"; + print "graph_category search\n"; print "graph_args -l 0 --base 1024\n"; print "used_memory.line $maxmemory:ff0000:Limit\n"; print "used_memory.label used memory\n"; @@ -189,7 +189,7 @@ switch ($0) { if ( $config ) { print "graph_title ${TITLE_PREFIX}Used keys\n"; print "graph_vlabel Used keys\n"; - print "graph_category redis\n"; + print "graph_category search\n"; print "graph_args -l 0\n"; foreach my $db (keys %{$dbs}) { diff --git a/plugins/redis/resque b/plugins/redis/resque index 38be29f2..23b75c73 100755 --- a/plugins/redis/resque +++ b/plugins/redis/resque @@ -48,7 +48,7 @@ my $opt = $0 ? $0 : 'default'; if ($opt eq 'failed') { if ($config) { print "graph_title Resque Failure rate\n"; - print "graph_category Resque\n"; + print "graph_category system\n"; print "graph_info This graph shows resque jobs that failed\n"; print "graph_args --lower-limit 0\n"; print 'graph_vlabel fails/s\n'; @@ -64,7 +64,7 @@ if ($opt eq 'failed') { elsif ($opt eq 'queues') { if ($config) { print "graph_title Resque queue rates\n"; - print "graph_category Resque\n"; + print "graph_category system\n"; print "graph_vlabel queue rates/s\n"; print "graph_info This graph monitors the in and out rate of the queues\n"; print "graph_args --lower-limit 0\n"; @@ -97,7 +97,7 @@ elsif ($opt eq 'queues') { elsif ($opt eq 'queues_size') { if ($config) { print "graph_title Resque queue current size\n"; - print "graph_category Resque\n"; + print "graph_category system\n"; print "graph_vlabel queue size\n"; print "graph_info This graph monitors the current queues size\n"; print "graph_args --lower-limit 0\n"; @@ -133,7 +133,7 @@ elsif ($opt eq 'queues_size') { elsif ($opt eq 'workers_count') { if ($config) { print "graph_title Resque Workers Count\n"; - print "graph_category Resque\n"; + print "graph_category system\n"; print "graph_info This graph shows number of resque workers\n"; print "graph_args --lower-limit 0\n"; print "graph_vlabel workers\n"; @@ -149,7 +149,7 @@ elsif ($opt eq 'workers_count') { elsif ($opt eq 'workers_working') { if ($config) { print "graph_title Resque Workers in use\n"; - print "graph_category Resque\n"; + print "graph_category system\n"; print "graph_info This graph shows the \%age of resque workers busy\n"; print "graph_args --lower-limit 0 --upper-limit 100\n"; print "graph_vlabel %\n"; diff --git a/plugins/relayd/relayd b/plugins/relayd/relayd index 8f1e1147..aef563ae 100755 --- a/plugins/relayd/relayd +++ b/plugins/relayd/relayd @@ -82,7 +82,7 @@ if ($cmd eq 'config') { print("graph_title Relayd host availability\n"); print("graph_args --upper-limit 100\n"); print("graph_vlabel % availability\n"); - print("graph_category Load balancer\n"); + print("graph_category loadbalancer\n"); print("graph_info Ratio of time when this host was up. This is provided by relayd itself (not averaged by this plugin)\n"); for my $host (@hosts) { my $clean = clean_host($host); @@ -92,7 +92,7 @@ if ($cmd eq 'config') { print("graph_title Relayd host incidents\n"); print("graph_args --lower-limit 0\n"); print("graph_vlabel down incidents\n"); - print("graph_category Load balancer\n"); + print("graph_category loadbalancer\n"); print("graph_info Number of times this host went down\n"); for my $host (@hosts) { my $clean = clean_host($host); diff --git a/plugins/requesttracker/rt_ticket_loadtime b/plugins/requesttracker/rt_ticket_loadtime index 9f0b9d5d..e7629329 100755 --- a/plugins/requesttracker/rt_ticket_loadtime +++ b/plugins/requesttracker/rt_ticket_loadtime @@ -89,7 +89,7 @@ if [ "$1" = "config" ]; then echo "graph_title RT ticket loadtime" echo "graph_args --base 1000 -l 0" echo "graph_vlabel Loadtime in seconds" - echo "graph_category requesttracker" + echo "graph_category other" echo "graph_info This graph shows the loadtime in seconds of RT ticket $ticket_id" echo "webui.label loadtime WebUI" echo "webui.max 300" diff --git a/plugins/rethinkdb/rethinkdb_node_io b/plugins/rethinkdb/rethinkdb_node_io new file mode 100755 index 00000000..91893568 --- /dev/null +++ b/plugins/rethinkdb/rethinkdb_node_io @@ -0,0 +1,145 @@ +#!/usr/bin/env python3 +""" + rethinkdb_node_io - A munin plugin for Linux to monitor the io count + per second on the local node + + This plugin is licensed under the AGPL 3.0 license + + AGPL 3.0 RubenKelevra + Author: @RubenKelevra - + + This plugin is written with the known limitation to a single instance per + host. Patches which remove this limitation are very welcome. + + If your port / host is somewhat else than the default + localhost:28015, and/or your database-server differes in name from + `hostname` (short hostname), you can add rethinkdb-node-io config vars + like: + [rethinkdb_*] + env.rethinkdb_port 12345 + env.rethinkdb_host localhost.com + env.rethinkdb_servername localhost.com + + The following munin configuration parameters are supported: + #%# family=auto contrib + #%# capabilities=autoconf + +""" + +from importlib.util import find_spec +from multiprocessing import Process +from os import environ as env +from shutil import which +from socket import gethostname +from sys import argv +from sys import exit as fatal_ +from sys import stderr + + +# functions +def fatal(status): + fatal_("ERROR: " + status) + + +def rclose_async(): + conn.close() + + +def check_load_rethinkdb() -> bool: + try: + rdb_spec = find_spec("rethinkdb") + if rdb_spec is None: + return False + return True + except: + fatal("Unknown error while try to load RethinkDB-Driver") + + +def eprint(*args, **kwargs): + print(*args, file=stderr, **kwargs) + + +def getFirstLine(respond): + assert isinstance(respond, net.DefaultCursor) + for e in respond: + return e + + +def print_config(servername): + print("graph_title RethinkDB on '%s'- Local Database IOPS and Queries" % servername) + print("graph_args --base 1000 -l 0") + print("graph_vlabel Operations / second") + print("graph_category db") + print("total_qps.label queries per sec") + print("total_qps.type COUNTER") + print("total_rdps.label read docs per sec") + print("total_rdps.type COUNTER") + print("total_wdps.label written docs per sec") + print("total_wdps.type COUNTER") + exit(0) + + +def check_autoconf() -> bool: + # this might be too easy, but gonna try. + if which("rethinkdb"): + return True + return False + + +if __name__ == '__main__': + try: + RETHINKDB_SERVERNAME = env['rethinkdb_servername'] + except: + RETHINKDB_SERVERNAME = gethostname() + + if len(argv) > 2: + fatal("unsupported argument count") + elif len(argv) == 2: + if str(argv[1]) == "config": + print_config(RETHINKDB_SERVERNAME) + elif str(argv[1]) == "autoconf": + if check_autoconf(): + print("yes") + else: + print("no") + if not check_load_rethinkdb(): + # FIXME: Correct display of error message when driver is missing should be checked + fatal("RethinkDB-Driver not available!") + exit(0) + else: + fatal("unsupported argument") + + if not check_load_rethinkdb(): + fatal("RethinkDB-Driver not available!") + from rethinkdb import net, connect, db + + # load environment + try: + RETHINKDB_PORT = env['rethinkdb_port'] + except: + RETHINKDB_PORT = "28015" + + try: + RETHINKDB_HOST = env['rethinkdb_host'] + except: + RETHINKDB_HOST = "localhost" + + try: + conn = connect(RETHINKDB_HOST, RETHINKDB_PORT) + except: + fatal("connection attempt to the rethinkdb-host \"%s\" via port \"%s\" failed" % ( + str(RETHINKDB_HOST), str(RETHINKDB_PORT))) + + query_engine_info = getFirstLine( + db('rethinkdb').table("stats").filter({"server": RETHINKDB_SERVERNAME}).pluck('query_engine').limit(1).run( + conn))['query_engine'] + + rclose = Process(target=rclose_async()) + rclose.start() + + print("total_qps.value %s" % (query_engine_info["queries_total"])) + print("total_rdps.value %s" % (query_engine_info["read_docs_total"])) + print("total_wdps.value %s" % (query_engine_info["written_docs_total"])) + + # wait for connection termination + rclose.join() diff --git a/plugins/riak/riak_fsm_time_95 b/plugins/riak/riak_fsm_time_95 old mode 100644 new mode 100755 index c00cd7fc..0df440f7 --- a/plugins/riak/riak_fsm_time_95 +++ b/plugins/riak/riak_fsm_time_95 @@ -27,7 +27,7 @@ def doConfig(): print "graph_title 95th percentile of FSM time" print "graph_args --base 1000" print "graph_vlabel gets (-) puts (+) in ms" - print "graph_category Riak" + print "graph_category other" print "graph_info Response time, in milliseconds, met or beaten by 95% of riak_kv_get_fsm executions." print "get_fsm_time_95.label vnode_gets" diff --git a/plugins/riak/riak_memory b/plugins/riak/riak_memory index c1bf08b5..5f99c0f0 100755 --- a/plugins/riak/riak_memory +++ b/plugins/riak/riak_memory @@ -38,7 +38,7 @@ def doConfig(): print "graph_title Riak memory" print "graph_args --base 1000 -l 0" print "graph_vlabel memory" - print "graph_category Riak" + print "graph_category other" for name in names: print name + ".label " + name diff --git a/plugins/riak/riak_node b/plugins/riak/riak_node index 1d3dcc89..cc193bc8 100755 --- a/plugins/riak/riak_node +++ b/plugins/riak/riak_node @@ -38,7 +38,7 @@ def doConfig(): print "graph_title Riak puts and gets" print "graph_args --base 1000 -l 0" print "graph_vlabel puts/gets" - print "graph_category Riak" + print "graph_category other" for name in names: print name + ".label " + name diff --git a/plugins/network/ag241-adsl b/plugins/router/ag241-adsl similarity index 100% rename from plugins/network/ag241-adsl rename to plugins/router/ag241-adsl diff --git a/plugins/network/arris-tm502g_ b/plugins/router/arris-tm502g_ similarity index 100% rename from plugins/network/arris-tm502g_ rename to plugins/router/arris-tm502g_ diff --git a/plugins/network/avm-fritzbox-wan-traffic b/plugins/router/avm-fritzbox-wan-traffic similarity index 98% rename from plugins/network/avm-fritzbox-wan-traffic rename to plugins/router/avm-fritzbox-wan-traffic index 04a2b949..adda3a73 100755 --- a/plugins/network/avm-fritzbox-wan-traffic +++ b/plugins/router/avm-fritzbox-wan-traffic @@ -27,15 +27,17 @@ if(isset($argv[1]) && $argv[1] == "autoconf") { echo "graph_category network\n"; echo "graph_info This graph shows the traffic of the AVM Fritz!Box WAN network interface. Please note that the traffic is shown in bits per second, not bytes.\n"; echo "down.label received\n"; - echo "down.type COUNTER\n"; + echo "down.type DERIVE\n"; echo "down.graph no\n"; echo "down.cdef down,8,*\n"; + echo "down.min 0\n"; echo "down.max 1000000000\n"; echo "up.label " . $data_prop['NewWANAccessType'] . " (" . $data_prop['NewPhysicalLinkStatus'] . ")\n"; - echo "up.type COUNTER\n"; + echo "up.type DERIVE\n"; echo "up.negative down\n"; echo "up.draw AREA\n"; echo "up.cdef up,8,*\n"; + echo "up.min 0\n"; echo "up.max 1000000000\n"; echo "up.info Traffic of the WAN interface.\n"; echo "maxdown.label received\n"; diff --git a/plugins/beboxsync/beboxstats b/plugins/router/beboxstats similarity index 100% rename from plugins/beboxsync/beboxstats rename to plugins/router/beboxstats diff --git a/plugins/beboxsync/beboxstats.expect b/plugins/router/beboxstats.expect similarity index 100% rename from plugins/beboxsync/beboxstats.expect rename to plugins/router/beboxstats.expect diff --git a/plugins/beboxsync/beboxsync b/plugins/router/beboxsync similarity index 100% rename from plugins/beboxsync/beboxsync rename to plugins/router/beboxsync diff --git a/plugins/modem/cisco-epc3010_ b/plugins/router/cisco-epc3010_ similarity index 98% rename from plugins/modem/cisco-epc3010_ rename to plugins/router/cisco-epc3010_ index 0beeaf04..e1b83638 100755 --- a/plugins/modem/cisco-epc3010_ +++ b/plugins/router/cisco-epc3010_ @@ -58,7 +58,7 @@ config) if [ $DIRECTION == "downstream" ]; then printf "graph_title Cisco EPC3010 Downstream measurements\n"; printf "graph_args -l 0 --base 1000\n" - printf "graph_category other\n" + printf "graph_category sensors\n" printf "graph_vlabel Small number is Power Level, large number is SNR\n" printf "graph_info Downstream Signal to Noise Ratio (dB) and Power Level (dBmV) from Cisco EPC3010 modem. Info is scraped from statuspage on the modem (http://192.168.100.1)\n" printf "channel_1_pwr.label Channel 1 (dBmV)\n" @@ -98,7 +98,7 @@ config) if [ "$DIRECTION" == "upstream" ]; then printf "graph_title Cisco EPC3010 Upstream measurements\n" printf "graph_args -l 30 --base 1000\n" - printf "graph_category other\n" + printf "graph_category sensors\n" printf "graph_vlabel Power level in dBmV\n" printf "graph_info Upstream Power Levels from Cisco EPC3010 modem,scraped from its statuspage (http://192.168.100.1)\n" printf "channel_1_pwr.label Channel 1 (dBmV)\n" diff --git a/plugins/network/cisco_bgp_ b/plugins/router/cisco_bgp_ similarity index 100% rename from plugins/network/cisco_bgp_ rename to plugins/router/cisco_bgp_ diff --git a/plugins/other/conexant_adsl b/plugins/router/conexant_adsl similarity index 98% rename from plugins/other/conexant_adsl rename to plugins/router/conexant_adsl index fa774d81..acf4b186 100755 --- a/plugins/other/conexant_adsl +++ b/plugins/router/conexant_adsl @@ -27,6 +27,7 @@ if [ "$1" = "config" ]; then echo 'graph_title ADSL Connection Statistics' echo 'graph_args --base 1000 -l 0' + echo 'graph_category network' echo 'graph_vlabel Daily Connection Statistics' echo 'st_dw_crc.label Downstream CRC count' echo 'st_up_crc.label Upstream CRC count' diff --git a/plugins/network/d-link-dir-655-router-statistics-plugin b/plugins/router/d-link-dir-655-router-statistics-plugin similarity index 100% rename from plugins/network/d-link-dir-655-router-statistics-plugin rename to plugins/router/d-link-dir-655-router-statistics-plugin diff --git a/plugins/network/dartybox b/plugins/router/dartybox similarity index 95% rename from plugins/network/dartybox rename to plugins/router/dartybox index 3deaf5e9..eeb77622 100755 --- a/plugins/network/dartybox +++ b/plugins/router/dartybox @@ -21,22 +21,22 @@ if [ "$1" = "config" ] then echo multigraph dbox_adsl_bw echo graph_title DartyBox Adsl Bandwidth - echo graph_category DartyBox + echo graph_category network echo up.label UpStream echo down.label DownStream echo multigraph dbox_adsl_att echo graph_title DartyBox Adsl Attenuation - echo graph_category DartyBox + echo graph_category network echo up.label UpStream echo down.label DownStream echo multigraph dbox_adsl_snr echo graph_title DartyBox Adsl SignalNoise Ratio - echo graph_category DartyBox + echo graph_category network echo up.label UpStream echo down.label DownStream echo multigraph dbox_adsl_pkt echo graph_title DartyBox Adsl Packets - echo graph_category DartyBox + echo graph_category network echo up.label UpStream echo down.label DownStream echo up_c.label UpStream "(Correctable)" @@ -57,7 +57,7 @@ then echo down_u.min 0 echo multigraph dbox_adsl_uptime echo graph_title DartyBox Adsl Uptime - echo graph_category DartyBox + echo graph_category network echo graph_vlabel days echo uptime.label Uptime diff --git a/plugins/network/dsl-connection-speed b/plugins/router/dsl-connection-speed similarity index 98% rename from plugins/network/dsl-connection-speed rename to plugins/router/dsl-connection-speed index d8f809bb..06984f1e 100755 --- a/plugins/network/dsl-connection-speed +++ b/plugins/router/dsl-connection-speed @@ -6,7 +6,7 @@ case $1 in graph_order downspeed upspeed graph_title DSL Connection Speed graph_args --base 1000 -l 1000 --upper-limit 42000 -graph_category Network +graph_category network graph_scale no graph_vlabel DSL up / down speed downspeed.label Down speed diff --git a/plugins/network/dsl-stats b/plugins/router/dsl-stats similarity index 98% rename from plugins/network/dsl-stats rename to plugins/router/dsl-stats index f83e026b..58b4bf20 100755 --- a/plugins/network/dsl-stats +++ b/plugins/router/dsl-stats @@ -6,7 +6,7 @@ case $1 in graph_order snrdown snrup pwrdown pwrup graph_title dsl stats graph_args --base 1000 -graph_category Network +graph_category network graph_scale no graph_vlabel DSL SNR and Power attndown.label Down Attenuation diff --git a/plugins/system/freeboxuptime b/plugins/router/freeboxuptime similarity index 98% rename from plugins/system/freeboxuptime rename to plugins/router/freeboxuptime index 35062672..c7dcb654 100755 --- a/plugins/system/freeboxuptime +++ b/plugins/router/freeboxuptime @@ -49,7 +49,7 @@ #%# family=manual #%# capabilities=autoconf -CACHE_FILE=/var/lib/munin/plugin-state/FreeboxUptime.cache +CACHE_FILE=$MUNIN_PLUGSTATE/FreeboxUptime.cache CACHE_HOURS=3 NMAP=$(which nmap) TCP_PORT=9100 diff --git a/plugins/network/modem-nvg510 b/plugins/router/modem-nvg510 similarity index 90% rename from plugins/network/modem-nvg510 rename to plugins/router/modem-nvg510 index e4f5fc2e..00f7ca78 100755 --- a/plugins/network/modem-nvg510 +++ b/plugins/router/modem-nvg510 @@ -28,30 +28,30 @@ use HTTP::Tiny; use constant { - down_rate => 0, - up_rate => 1, - sn_down => 14, - sn_up => 15, - line_attn_down => 16, - line_attn_up => 17, - power_down => 18, - power_up => 19, - err_sec_down => 20, - err_sec_up => 21, - los_down => 22, - los_up => 23, - lof_down => 24, - lof_up => 25, - fec_down => 26, - fec_up => 27, - crc_down => 28, - crc_up => 29, + down_rate => 2, + up_rate => 3, + sn_down => 23, + sn_up => 24, + line_attn_down => 25, + line_attn_up => 26, + power_down => 27, + power_up => 28, + err_sec_down => 29, + err_sec_up => 30, + los_down => 31, + los_up => 32, + lof_down => 33, + lof_up => 34, + fec_down => 35, + fec_up => 36, + crc_down => 37, + crc_up => 38, }; if(defined $ARGV[0] and $ARGV[0] eq 'autoconf'){ my $url = $ENV{url} || "http://192.168.1.254/cgi-bin/dslstatistics.ha"; - my $html = HTTP::Tiny->new(timeout => 1 )->get($url); + my $html = HTTP::Tiny->new(timeout => 30 )->get($url); if($html->{success} && $html->{content} =~ m{Broadband Status}){ print "yes\n"; @@ -159,10 +159,11 @@ lof_up.min 0 ########################## MAIN ############################# my $url = $ENV{url} || "http://192.168.1.254/cgi-bin/dslstatistics.ha"; -my $html = HTTP::Tiny->new(timeout => 1 )->get($url); +my $html = HTTP::Tiny->new(timeout => 30 )->get($url); die "Couldn't fetch $url" unless $html->{success}; -my @stats = $html->{content} =~ m{\s*([\d\.]+)\s*}g; +my @stats = $html->{content} =~ m{(.*?)}sg; +chomp(@stats); print qq|multigraph nvg510_speed down_rate.value $stats[down_rate] diff --git a/plugins/router/motorola_sb6141 b/plugins/router/motorola_sb6141 new file mode 100755 index 00000000..54452f99 --- /dev/null +++ b/plugins/router/motorola_sb6141 @@ -0,0 +1,170 @@ +#!/usr/bin/env python3 + +# This plugin graphs the following values of the Motorola SB6141 cable +# modem: +# +# * upstream and downstream power levels +# * downstream signal to noise ratio +# * downstream signal statistics (codeword counts) +# +# The values are retrieved from the cable modem's status web pages at +# 192.168.100.1. So, this plugin must be installed on a munin node +# which can access those pages. +# +# To install, place this plugin in the node's plugins directory, +# /etc/munin/plugins and restart munin-node. +# +# Developed and tested with firmware SB_KOMODO-1.0.6.16-SCM00-NOSH +# (build time Feb 16 2016 11:28:04), hardware version 7.0, boot +# version PSPU-Boot(25CLK) 1.0.12.18m3. +# +# Requires the multigraph and dirtyconfig capabilities available in +# munin 2.0 and newer. +# +# Copyright © 2016 Kenyon Ralph +# +# This program is free software. It comes without any warranty, to the +# extent permitted by applicable law. You can redistribute it and/or +# modify it under the terms of the Do What The Fuck You Want To Public +# License, Version 2, as published by Sam Hocevar. See +# http://www.wtfpl.net/ for more details. +# +# The latest version of this plugin can be found in the munin contrib +# repository at https://github.com/munin-monitoring/contrib. Issues +# with this plugin may be reported there. Patches accepted through the +# normal github process of forking the repository and submitting a +# pull request with your commits. + +import html.parser +import urllib.request +import sys + +class MotorolaHTMLParser(html.parser.HTMLParser): + def __init__(self): + html.parser.HTMLParser.__init__(self) + self.signaldatapage = list() + self.downstream_channels = list() + self.downstream_SNRs = list() + self.downstream_powers = list() + self.upstream_channels = list() + self.upstream_powers = list() + self.unerrored_codewords = list() + self.correctable_codewords = list() + self.uncorrectable_codewords = list() + + def handle_data(self, data): + data = data.strip() + if data != '': self.signaldatapage.append(data) + + def process(self): + # first and last elements are just javascript + del self.signaldatapage[0] + del self.signaldatapage[-1] + + di = iter(self.signaldatapage) + + element = next(di) + while element != 'Channel ID': element = next(di) + + while element != 'Frequency': + element = next(di) + if element != 'Frequency': self.downstream_channels.append(element) + + while element != 'Signal to Noise Ratio': element = next(di) + + while element != 'Downstream Modulation': + element = next(di) + if element != 'Downstream Modulation': self.downstream_SNRs.append(element.split()[0]) + + while element != 'The Downstream Power Level reading is a snapshot taken at the time this page was requested. Please Reload/Refresh this Page for a new reading': element = next(di) + + while element != 'Upstream': + element = next(di) + if element != 'Upstream': self.downstream_powers.append(element.split()[0]) + + while element != 'Channel ID': element = next(di) + + while element != 'Frequency': + element = next(di) + if element != 'Frequency': self.upstream_channels.append(element) + + while element != 'Power Level': element = next(di) + + while element != 'Upstream Modulation': + element = next(di) + if element != 'Upstream Modulation': self.upstream_powers.append(element.split()[0]) + + while element != 'Total Unerrored Codewords': element = next(di) + + while element != 'Total Correctable Codewords': + element = next(di) + if element != 'Total Correctable Codewords': self.unerrored_codewords.append(element) + + while element != 'Total Uncorrectable Codewords': + element = next(di) + if element != 'Total Uncorrectable Codewords': self.correctable_codewords.append(element) + + while True: + try: + element = next(di) + self.uncorrectable_codewords.append(element) + except StopIteration: + break + +def main(): + if len(sys.argv) != 2 or sys.argv[1] != 'config': + print('Error: plugin designed for the dirtyconfig protocol, must be run with the config argument') + sys.exit(1) + + parser = MotorolaHTMLParser() + for line in urllib.request.urlopen("http://192.168.100.1/cmSignalData.htm"): + parser.feed(line.decode()) + parser.process() + + print('multigraph motorola_sb6141_power') + print('graph_title Motorola SB6141 Cable Modem Power') + print('graph_vlabel Signal Strength (dBmV)') + print('graph_info This graph shows the downstream and upstream power reported by a Motorola SB6141 cable modem.') + print('graph_category network') + for c, p in zip(parser.downstream_channels, parser.downstream_powers): + print('ds_power_{0}.label Channel {0} Downstream Power'.format(c)) + print('ds_power_{0}.type GAUGE'.format(c)) + print('ds_power_{0}.value {1}'.format(c, p)) + for c, p in zip(parser.upstream_channels, parser.upstream_powers): + print('us_power_{0}.label Channel {0} Upstream Power'.format(c)) + print('us_power_{0}.type GAUGE'.format(c)) + print('us_power_{0}.value {1}'.format(c, p)) + + print('multigraph motorola_sb6141_snr') + print('graph_title Motorola SB6141 Cable Modem SNR') + print('graph_vlabel Signal-to-Noise Ratio (dB)') + print('graph_info This graph shows the downstream signal-to-noise ratio reported by a Motorola SB6141 cable modem.') + print('graph_category network') + for c, snr in zip(parser.downstream_channels, parser.downstream_SNRs): + print('snr_chan_{0}.label Channel {0} SNR'.format(c)) + print('snr_chan_{0}.type GAUGE'.format(c)) + print('snr_chan_{0}.value {1}'.format(c, snr)) + + print('multigraph motorola_sb6141_codewords') + print('graph_title Motorola SB6141 Cable Modem Codewords') + print('graph_vlabel Codewords/${graph_period}') + print('graph_info This graph shows the downstream codeword rates reported by a Motorola SB6141 cable modem.') + print('graph_category network') + for c, unerr in zip(parser.downstream_channels, parser.unerrored_codewords): + print('unerr_chan_{0}.label Channel {0} Unerrored Codewords'.format(c)) + print('unerr_chan_{0}.type DERIVE'.format(c)) + print('unerr_chan_{0}.min 0'.format(c)) + print('unerr_chan_{0}.value {1}'.format(c, unerr)) + for c, corr in zip(parser.downstream_channels, parser.correctable_codewords): + print('corr_chan_{0}.label Channel {0} Correctable Codewords'.format(c)) + print('corr_chan_{0}.type DERIVE'.format(c)) + print('corr_chan_{0}.min 0'.format(c)) + print('corr_chan_{0}.value {1}'.format(c, corr)) + for c, uncorr in zip(parser.downstream_channels, parser.uncorrectable_codewords): + print('uncorr_chan_{0}.label Channel {0} Uncorrectable Codewords'.format(c)) + print('uncorr_chan_{0}.type DERIVE'.format(c)) + print('uncorr_chan_{0}.min 0'.format(c)) + print('uncorr_chan_{0}.value {1}'.format(c, uncorr)) + +if __name__ == "__main__": + main() diff --git a/plugins/snmp/snmp__cisco_sbs_cpu b/plugins/router/snmp__cisco_sbs_cpu old mode 100644 new mode 100755 similarity index 100% rename from plugins/snmp/snmp__cisco_sbs_cpu rename to plugins/router/snmp__cisco_sbs_cpu diff --git a/plugins/snmp/snmp__juniper b/plugins/router/snmp__juniper similarity index 92% rename from plugins/snmp/snmp__juniper rename to plugins/router/snmp__juniper index 44ee5ae3..da332ea8 100755 --- a/plugins/snmp/snmp__juniper +++ b/plugins/router/snmp__juniper @@ -109,8 +109,7 @@ class JunOSSnmpClient(object): if not str(name).startswith(jnxOperatingTable): continue - # TODO: Find a better way to get the routing engines in a cluster - if str(value).endswith(' Routing Engine'): + if 'Routing Engine' in str(value): devices[str(name)[len(jnxOperatingTable):]] = re.sub("[^\w]", '_', str(value).replace(' Routing Engine', '')) return devices @@ -142,19 +141,19 @@ class JunOSSnmpClient(object): devices = self.get_devices() data_def = [ - ('temp', self.hostname, 'System temperature', '--base 1000', 'System temperature in C', 'system'), - ('cpu', self.hostname, 'CPU usage', '--base 1000 -l 0 --upper-limit 100', 'CPU usage in %', 'system'), - ('buffer', self.hostname, 'Buffer usage', '--base 1000 -l 0 --upper-limit 100', 'Buffer usage in %', 'system'), + ('temp', self.hostname, 'System temperature', '--base 1000', 'System temperature in C'), + ('cpu', self.hostname, 'CPU usage', '--base 1000 -l 0 --upper-limit 100', 'CPU usage in %'), + ('buffer', self.hostname, 'Buffer usage', '--base 1000 -l 0 --upper-limit 100', 'Buffer usage in %'), ] - for datarow, hostname, title, args, vlabel, category in data_def: + for datarow, hostname, title, args, vlabel in data_def: print """multigraph juniper_{datarow} host_name {hostname} graph_title {title} graph_vlabel {vlabel} graph_args {args} -graph_category {category} -graph_info {title}""".format(datarow=datarow, hostname=hostname, title=title, args=args, vlabel=vlabel, category=category) +graph_category fw +graph_info {title}""".format(datarow=datarow, hostname=hostname, title=title, args=args, vlabel=vlabel) for suffix, node in devices.iteritems(): ident = "%s_%s" % (datarow, node) diff --git a/plugins/snmp/snmp__juniper_spu b/plugins/router/snmp__juniper_spu similarity index 100% rename from plugins/snmp/snmp__juniper_spu rename to plugins/router/snmp__juniper_spu diff --git a/plugins/snmp/snmp__linksys_poe b/plugins/router/snmp__linksys_poe similarity index 100% rename from plugins/snmp/snmp__linksys_poe rename to plugins/router/snmp__linksys_poe diff --git a/plugins/snmp/snmp__screenos b/plugins/router/snmp__screenos similarity index 100% rename from plugins/snmp/snmp__screenos rename to plugins/router/snmp__screenos diff --git a/plugins/snmp/snmp_zyxel_usg__cpu b/plugins/router/snmp_zyxel_usg__cpu similarity index 99% rename from plugins/snmp/snmp_zyxel_usg__cpu rename to plugins/router/snmp_zyxel_usg__cpu index 81d7ec7c..71d1d628 100755 --- a/plugins/snmp/snmp_zyxel_usg__cpu +++ b/plugins/router/snmp_zyxel_usg__cpu @@ -81,7 +81,7 @@ if ($ARGV[0] and $ARGV[0] eq "config") die "Croaking: " . $session->error(); } print "graph_title CPU usage\n"; - print "graph_category system\n"; + print "graph_category cpu\n"; print "graph_args --base 1000 -r --lower-limit 0 --upper-limit 100\n"; print "graph_vlabel %\n"; print "graph_scale no\n"; diff --git a/plugins/snmp/snmp_zyxel_usg__ram b/plugins/router/snmp_zyxel_usg__ram similarity index 98% rename from plugins/snmp/snmp_zyxel_usg__ram rename to plugins/router/snmp_zyxel_usg__ram index f0bcd28f..c3dc1c7a 100755 --- a/plugins/snmp/snmp_zyxel_usg__ram +++ b/plugins/router/snmp_zyxel_usg__ram @@ -78,7 +78,7 @@ if ($ARGV[0] and $ARGV[0] eq "config") die "Croaking: " . $session->error(); } print "graph_title RAM usage\n"; - print "graph_category system\n"; + print "graph_category memory\n"; print "graph_args --base 1000 -r --lower-limit 0 --upper-limit 100\n"; print "graph_vlabel %\n"; print "graph_scale no\n"; diff --git a/plugins/snmp/snmp_zyxel_usg__sessions b/plugins/router/snmp_zyxel_usg__sessions similarity index 100% rename from plugins/snmp/snmp_zyxel_usg__sessions rename to plugins/router/snmp_zyxel_usg__sessions diff --git a/plugins/snmp/snmp_zyxel_zywall__cpu b/plugins/router/snmp_zyxel_zywall__cpu similarity index 98% rename from plugins/snmp/snmp_zyxel_zywall__cpu rename to plugins/router/snmp_zyxel_zywall__cpu index 86813c44..0d00e3ed 100755 --- a/plugins/snmp/snmp_zyxel_zywall__cpu +++ b/plugins/router/snmp_zyxel_zywall__cpu @@ -78,7 +78,7 @@ if ($ARGV[0] and $ARGV[0] eq "config") die "Croaking: " . $session->error(); } print "graph_title CPU usage\n"; - print "graph_category system\n"; + print "graph_category cpu\n"; print "graph_args --base 1000 -r --lower-limit 0 --upper-limit 100\n"; print "graph_vlabel %\n"; print "graph_scale no\n"; diff --git a/plugins/snmp/snmp_zyxel_zywall__flash b/plugins/router/snmp_zyxel_zywall__flash similarity index 100% rename from plugins/snmp/snmp_zyxel_zywall__flash rename to plugins/router/snmp_zyxel_zywall__flash diff --git a/plugins/snmp/snmp_zyxel_zywall__ram b/plugins/router/snmp_zyxel_zywall__ram similarity index 98% rename from plugins/snmp/snmp_zyxel_zywall__ram rename to plugins/router/snmp_zyxel_zywall__ram index aa501ecd..fb27ad89 100755 --- a/plugins/snmp/snmp_zyxel_zywall__ram +++ b/plugins/router/snmp_zyxel_zywall__ram @@ -78,7 +78,7 @@ if ($ARGV[0] and $ARGV[0] eq "config") die "Croaking: " . $session->error(); } print "graph_title RAM usage\n"; - print "graph_category system\n"; + print "graph_category memory\n"; print "graph_args --base 1000 -r --lower-limit 0 --upper-limit 100\n"; print "graph_vlabel %\n"; print "graph_scale no\n"; diff --git a/plugins/snmp/snmp_zyxel_zywall__sessions b/plugins/router/snmp_zyxel_zywall__sessions similarity index 100% rename from plugins/snmp/snmp_zyxel_zywall__sessions rename to plugins/router/snmp_zyxel_zywall__sessions diff --git a/plugins/network/speedport_300 b/plugins/router/speedport_300 similarity index 97% rename from plugins/network/speedport_300 rename to plugins/router/speedport_300 index c7e5d95c..4b5cd531 100755 --- a/plugins/network/speedport_300 +++ b/plugins/router/speedport_300 @@ -29,7 +29,7 @@ echo 'graph_args --base 1000 -l 0' echo 'graph_scale no' echo 'graph_vlabel Up- / Downstream in kBit/s' - echo 'graph_category Http' + echo 'graph_category network' echo 'download.label Downstream' echo 'upload.label Upstream' echo 'graph_info Information from the top_status.htm of the Speedport 300' diff --git a/plugins/network/tg585v7__ b/plugins/router/tg585v7__ similarity index 100% rename from plugins/network/tg585v7__ rename to plugins/router/tg585v7__ diff --git a/plugins/rsync/rsyncd_bytes b/plugins/rsync/rsyncd_bytes index 6613ea24..d290282b 100755 --- a/plugins/rsync/rsyncd_bytes +++ b/plugins/rsync/rsyncd_bytes @@ -16,7 +16,7 @@ mktemp -t $1 RSYNCD_LOG=${logfile:-/var/log/rsyncd.log} LOGTAIL=${logtail:-`which logtail`} -STATEFILE=/var/lib/munin/plugin-state/rsync-bytes.offset +STATEFILE=$MUNIN_PLUGSTATE/rsync-bytes.offset if [ "$1" = "autoconf" ]; then if [ -f "${RSYNCD_LOG}" -a -n "${LOGTAIL}" -a -x "${LOGTAIL}" ] ; then @@ -32,7 +32,7 @@ if [ "$1" = "config" ]; then echo 'graph_title Rsync Server Bytes' echo 'graph_args --base 1000 -l 0' echo 'graph_order send recv' - echo 'graph_category rsync' + echo 'graph_category filetransfer' echo 'graph_vlabel Rsync Bytes' echo 'send.label Bytes Send' echo 'recv.label Bytes Recv' diff --git a/plugins/rsync/rsyncd_count b/plugins/rsync/rsyncd_count index e8d98905..9b3a8afd 100755 --- a/plugins/rsync/rsyncd_count +++ b/plugins/rsync/rsyncd_count @@ -16,7 +16,7 @@ mktemp -t $1 RSYNCD_LOG=${logfile:-/var/log/rsyncd.log} LOGTAIL=${logtail:-`which logtail`} -STATEFILE=/var/lib/munin/plugin-state/rsync-count.offset +STATEFILE=$MUNIN_PLUGSTATE/rsync-count.offset if [ "$1" = "autoconf" ]; then if [ -f "${RSYNCD_LOG}" -a -n "${LOGTAIL}" -a -x "${LOGTAIL}" ] ; then @@ -32,7 +32,7 @@ if [ "$1" = "config" ]; then echo 'graph_title Rsync Server Transfers' echo 'graph_args --base 1000 -l 0' echo 'graph_order send recv' - echo 'graph_category rsync' + echo 'graph_category filetransfer' echo 'graph_vlabel Rsync Server Transfers' echo 'send.label Files Send' echo 'recv.label Files Recv' diff --git a/plugins/rtorrent/rtom_allsessions_mem b/plugins/rtorrent/rtom_allsessions_mem index 1885a62b..1b12be09 100755 --- a/plugins/rtorrent/rtom_allsessions_mem +++ b/plugins/rtorrent/rtom_allsessions_mem @@ -3,7 +3,7 @@ # xmlrpc based munin plugin for monitoring rtorrent's memory usage # prerequisites: # - rtorrent 0.7.5 or newer compiled with --with-xmlrpc-c -# check http://libtorrent.rakshasa.no/wiki/RTorrentXMLRPCGuide for further informations +# check http://libtorrent.rakshasa.no/wiki/RTorrentXMLRPCGuide for further information # # written by Gabor Hudiczius # web: http://projects.cyla.homeip.net/rtwi/wiki/rTorrentOMeter @@ -53,7 +53,7 @@ if ( $ARGV[0] and $ARGV[0] eq "config" ) { print "graph_title rTorrent memory usage\n"; print "graph_args --base 1024 --lower-limit 0\n"; print "graph_vlabel Bytes\n"; - print "graph_category torrent ".${category}."\n"; + print "graph_category filetransfer".${category}."\n"; print "mem.label Memory usage\n"; print "mem.info Memory usage of rTorrent\n"; print "mem.type GAUGE\n"; diff --git a/plugins/rtorrent/rtom_allsessions_peers b/plugins/rtorrent/rtom_allsessions_peers index ef32c539..a3cc1705 100755 --- a/plugins/rtorrent/rtom_allsessions_peers +++ b/plugins/rtorrent/rtom_allsessions_peers @@ -3,7 +3,7 @@ # xmlrpc based munin plugin for monitoring rtorrent's peer count # prerequisites: # - rtorrent 0.7.5 or newer compiled with --with-xmlrpc-c -# check http://libtorrent.rakshasa.no/wiki/RTorrentXMLRPCGuide for further informations +# check http://libtorrent.rakshasa.no/wiki/RTorrentXMLRPCGuide for further information # # written by Gabor Hudiczius # web: http://projects.cyla.homeip.net/rtwi/wiki/rTorrentOMeter @@ -51,7 +51,7 @@ if ( $ARGV[0] and $ARGV[0] eq "config" ) { print "graph_title rTorrent peer statistics\n"; print "graph_args --base 1000 --lower-limit 0\n"; print "graph_vlabel peers\n"; - print "graph_category torrent ".${category}."\n"; + print "graph_category filetransfer".${category}."\n"; print "outgoing.label outgoing\n"; print "outgoing.draw AREA\n"; print "outgoing.info number of outgoing connections\n"; diff --git a/plugins/rtorrent/rtom_allsessions_spdd b/plugins/rtorrent/rtom_allsessions_spdd index c917bccc..ed89061a 100755 --- a/plugins/rtorrent/rtom_allsessions_spdd +++ b/plugins/rtorrent/rtom_allsessions_spdd @@ -3,7 +3,7 @@ # xmlrpc based munin plugin for monitoring rtorrent's upload/download speed # prerequisites: # - rtorrent 0.7.5 or newer compiled with --with-xmlrpc-c -# check http://libtorrent.rakshasa.no/wiki/RTorrentXMLRPCGuide for further informations +# check http://libtorrent.rakshasa.no/wiki/RTorrentXMLRPCGuide for further information # # written by Gabor Hudiczius # web: http://projects.cyla.homeip.net/rtwi/wiki/rTorrentOMeter @@ -65,7 +65,7 @@ if ( $ARGV[0] and $ARGV[0] eq "config" ) { print "graph_title rTorrent speeds\n"; print "graph_args --base 1024\n"; print "graph_vlabel Bytes per \${graph_period}\n"; - print "graph_category torrent ".${category}."\n"; + print "graph_category filetransfer".${category}."\n"; print "down.label Download B/s\n"; print "down.info Download speed in Bytes per seconds\n"; print "down.type DERIVE\n"; diff --git a/plugins/rtorrent/rtom_allsessions_vol b/plugins/rtorrent/rtom_allsessions_vol index 3fafa7d3..830575f4 100755 --- a/plugins/rtorrent/rtom_allsessions_vol +++ b/plugins/rtorrent/rtom_allsessions_vol @@ -3,7 +3,7 @@ # xmlrpc based munin plugin for monitoring rtorrent's torrent count # prerequisites: # - rtorrent 0.7.5 or newer compiled with --with-xmlrpc-c -# check http://libtorrent.rakshasa.no/wiki/RTorrentXMLRPCGuide for further informations +# check http://libtorrent.rakshasa.no/wiki/RTorrentXMLRPCGuide for further information # # written by Gabor Hudiczius # web: http://projects.cyla.homeip.net/rtwi/wiki/rTorrentOMeter @@ -52,7 +52,7 @@ if ( $ARGV[0] and $ARGV[0] eq "config" ) { print "graph_args --base 1000 -r --lower-limit 0\n"; print "graph_title rTorrent volume\n"; print "graph_vlabel active torrents\n"; - print "graph_category torrent ".${category}."\n"; + print "graph_category filetransfer".${category}."\n"; print "complete.label complete\n"; print "complete.draw AREA\n"; print "complete.info complete torrents\n"; diff --git a/plugins/rtorrent/rtom_mem b/plugins/rtorrent/rtom_mem index fb393c0b..eb8a5850 100755 --- a/plugins/rtorrent/rtom_mem +++ b/plugins/rtorrent/rtom_mem @@ -3,7 +3,7 @@ # xmlrpc based munin plugin for monitoring rtorrent's memory usage # prerequisites: # - rtorrent 0.7.5 or newer compiled with --with-xmlrpc-c -# check http://libtorrent.rakshasa.no/wiki/RTorrentXMLRPCGuide for further informations +# check http://libtorrent.rakshasa.no/wiki/RTorrentXMLRPCGuide for further information # # written by Gabor Hudiczius # web: http://projects.cyla.homeip.net/rtwi/wiki/rTorrentOMeter @@ -53,7 +53,7 @@ if ( $ARGV[0] and $ARGV[0] eq "config" ) { print "graph_title rTorrent memory usage\n"; print "graph_args --base 1024 --lower-limit 0\n"; print "graph_vlabel Bytes\n"; - print "graph_category torrent ".${category}."\n"; + print "graph_category filetransfer".${category}."\n"; print "mem.label Memory usage\n"; print "mem.info Memory osage of rTorrent\n"; print "mem.type GAUGE\n"; diff --git a/plugins/rtorrent/rtom_peers b/plugins/rtorrent/rtom_peers index ae71ea2e..dace07bb 100755 --- a/plugins/rtorrent/rtom_peers +++ b/plugins/rtorrent/rtom_peers @@ -3,7 +3,7 @@ # xmlrpc based munin plugin for monitoring rtorrent's peer count # prerequisites: # - rtorrent 0.7.5 or newer compiled with --with-xmlrpc-c -# check http://libtorrent.rakshasa.no/wiki/RTorrentXMLRPCGuide for further informations +# check http://libtorrent.rakshasa.no/wiki/RTorrentXMLRPCGuide for further information # # written by Gabor Hudiczius # web: http://projects.cyla.homeip.net/rtwi/wiki/rTorrentOMeter @@ -53,7 +53,7 @@ if ( $ARGV[0] and $ARGV[0] eq "config" ) { print "graph_title rTorrent peer statistics\n"; print "graph_args --base 1000 --lower-limit 0\n"; print "graph_vlabel peers\n"; - print "graph_category torrent ".${category}."\n"; + print "graph_category filetransfer".${category}."\n"; print "outgoing.label outgoing\n"; print "outgoing.draw AREA\n"; print "outgoing.info number of outgoing connections\n"; diff --git a/plugins/rtorrent/rtom_spdd b/plugins/rtorrent/rtom_spdd index 3c23ca7d..cbb06c14 100755 --- a/plugins/rtorrent/rtom_spdd +++ b/plugins/rtorrent/rtom_spdd @@ -3,7 +3,7 @@ # xmlrpc based munin plugin for monitoring rtorrent's upload/download speed # prerequisites: # - rtorrent 0.7.5 or newer compiled with --with-xmlrpc-c -# check http://libtorrent.rakshasa.no/wiki/RTorrentXMLRPCGuide for further informations +# check http://libtorrent.rakshasa.no/wiki/RTorrentXMLRPCGuide for further information # # written by Gabor Hudiczius # web: http://projects.cyla.homeip.net/rtwi/wiki/rTorrentOMeter @@ -68,7 +68,7 @@ if ( $ARGV[0] and $ARGV[0] eq "config" ) { print "graph_title rTorrent speeds\n"; print "graph_args --base 1024\n"; print "graph_vlabel Bytes per \${graph_period}\n"; - print "graph_category torrent ".${category}."\n"; + print "graph_category filetransfer".${category}."\n"; print "down.label Download B/s\n"; print "down.info Download speed in Bytes per seconds\n"; print "down.type DERIVE\n"; diff --git a/plugins/rtorrent/rtom_vol b/plugins/rtorrent/rtom_vol index c067231b..f8e08e33 100755 --- a/plugins/rtorrent/rtom_vol +++ b/plugins/rtorrent/rtom_vol @@ -3,7 +3,7 @@ # xmlrpc based munin plugin for monitoring rtorrent's torrent count # prerequisites: # - rtorrent 0.7.5 or newer compiled with --with-xmlrpc-c -# check http://libtorrent.rakshasa.no/wiki/RTorrentXMLRPCGuide for further informations +# check http://libtorrent.rakshasa.no/wiki/RTorrentXMLRPCGuide for further information # # written by Gabor Hudiczius # web: http://projects.cyla.homeip.net/rtwi/wiki/rTorrentOMeter @@ -54,7 +54,7 @@ if ( $ARGV[0] and $ARGV[0] eq "config" ) { print "graph_args --base 1000 -r --lower-limit 0\n"; print "graph_title rTorrent volume\n"; print "graph_vlabel active torrents\n"; - print "graph_category torrent ".${category}."\n"; + print "graph_category filetransfer".${category}."\n"; print "complete.label complete\n"; print "complete.draw AREA\n"; print "complete.info complete torrents\n"; diff --git a/plugins/s3/s3_items b/plugins/s3/s3_items index 2c7277d3..44511240 100755 --- a/plugins/s3/s3_items +++ b/plugins/s3/s3_items @@ -66,7 +66,7 @@ if ( $ARGV[0] eq "config" ) { # The headers print "graph_title S3 Items\n"; - print "graph_category s3\n"; + print "graph_category cloud\n"; print "graph_vlabel items\n"; print 'graph_info Plugin available at http://www.ohardt.com/dev/munin/' . "\n"; diff --git a/plugins/s3/s3_storage b/plugins/s3/s3_storage index e579dba3..ac73a125 100755 --- a/plugins/s3/s3_storage +++ b/plugins/s3/s3_storage @@ -61,7 +61,7 @@ if ($ARGV[0] and $ARGV[0] eq "config") { # The headers print "graph_title S3 Storage Usage\n"; - print "graph_category s3\n"; + print "graph_category cloud\n"; print "graph_args --base 1024 -l 0\n"; print "graph_vlabel bytes\n"; print "graph_info Plugin available at https://github.com/aptivate/munin-contrib/blob/master/plugins/s3/s3_storage\n"; diff --git a/plugins/sabnzbd/sabnzbd_dataleft b/plugins/sabnzbd/sabnzbd_dataleft old mode 100644 new mode 100755 index 41bad9b8..1f56ea8b --- a/plugins/sabnzbd/sabnzbd_dataleft +++ b/plugins/sabnzbd/sabnzbd_dataleft @@ -44,7 +44,7 @@ if(defined $ARGV[0] && $ARGV[0] eq 'config') print <XMLin($vals); #get/output vals my $left = $xmlvals->{mbleft}; $left =~ /(\d+)\./; -print "rem.value ".$1."\n"; \ No newline at end of file +print "rem.value ".$1."\n"; diff --git a/plugins/sabnzbd/sabnzbd_speed b/plugins/sabnzbd/sabnzbd_speed old mode 100644 new mode 100755 index 474e8def..fb466c7b --- a/plugins/sabnzbd/sabnzbd_speed +++ b/plugins/sabnzbd/sabnzbd_speed @@ -44,7 +44,7 @@ if(defined $ARGV[0] && $ARGV[0] eq 'config') print <XMLin($vals); #get/output vals my $speed = $xmlvals->{kbpersec}; $speed =~ /(\d+)\./; -print "speed.value ".$1."\n"; \ No newline at end of file +print "speed.value ".$1."\n"; diff --git a/plugins/samba/samba_locked b/plugins/samba/samba_locked index 787de0c6..d591510e 100755 --- a/plugins/samba/samba_locked +++ b/plugins/samba/samba_locked @@ -9,7 +9,7 @@ # # $Log$ # Revision 1.0 2007/04/16 Jon Higgs -# Initial Release - Adapated from jimmyo's processses plugin. +# Initial Release - Adapted from jimmyo's processses plugin. # # Magick markers (optional - used by munin-config and som installation # scripts): @@ -26,7 +26,7 @@ if [ "$1" = "config" ]; then echo 'graph_title Samba Locked Files' echo 'graph_args --base 1000 -l 0 ' echo 'graph_vlabel number of locked files' - echo 'graph_category Samba' + echo 'graph_category fs' echo 'graph_info This graph shows the number locked Samba Files.' echo 'samba_locked.label Locked Files' echo 'samba_locked.draw LINE2' diff --git a/plugins/samba/samba_users b/plugins/samba/samba_users index d43510bb..4bf00d56 100755 --- a/plugins/samba/samba_users +++ b/plugins/samba/samba_users @@ -9,7 +9,7 @@ # # $Log$ # Revision 1.0 2007/04/16 Jon Higgs -# Initial Release - Adapated from jimmyo's processses plugin. +# Initial Release - Adapted from jimmyo's processses plugin. # # Revision 1.1 2014/07/24 MangaII # Add exit 0 @@ -32,7 +32,7 @@ if [ "$1" = "config" ]; then echo 'graph_title Samba Users' echo 'graph_args --base 1000 -l 0 ' echo 'graph_vlabel number of Samba users.' - echo 'graph_category Samba' + echo 'graph_category fs' echo 'graph_info This graph shows the number Samba users.' echo 'samba_users.label Samba Users' echo 'samba_users.draw LINE2' diff --git a/plugins/system/iostat-cputps-average b/plugins/sar/iostat-cputps-average similarity index 100% rename from plugins/system/iostat-cputps-average rename to plugins/sar/iostat-cputps-average diff --git a/plugins/scalix/scalix_clients b/plugins/scalix/scalix_clients index a0c92d1a..a4bdfc88 100755 --- a/plugins/scalix/scalix_clients +++ b/plugins/scalix/scalix_clients @@ -13,7 +13,7 @@ if [ "$1" = "config" ]; then echo 'graph_title Scalix client connections' echo 'graph_args --lower-limit 0' echo 'graph_vlabel number' - echo 'graph_category Scalix' + echo 'graph_category mail' echo 'swa.label Webmail connections' echo 'swa.draw AREA' echo 'imap.label IMAP connections' diff --git a/plugins/scalix/scalix_indexwork b/plugins/scalix/scalix_indexwork index e513780c..2b1a0a57 100755 --- a/plugins/scalix/scalix_indexwork +++ b/plugins/scalix/scalix_indexwork @@ -23,7 +23,7 @@ if($mode eq "config") { print "graph_title Scalix Index Work Queue\n"; print "graph_args -l 0\n"; print "graph_vlabel number\n"; - print "graph_category Scalix\n"; + print "graph_category mail\n"; print "graph_title Scalix Index Work Queue\n"; print "graph_vlabel Items\n"; print "items.label Items to be indexed\n"; diff --git a/plugins/scalix/scalix_processes b/plugins/scalix/scalix_processes index c96aaccf..6c39052e 100755 --- a/plugins/scalix/scalix_processes +++ b/plugins/scalix/scalix_processes @@ -11,7 +11,7 @@ if [ "$1" = "config" ]; then echo 'graph_title Scalix processes' echo 'graph_args --lower-limit 0' echo 'graph_vlabel number' - echo 'graph_category Scalix' + echo 'graph_category mail' echo 'mimeb.label MIME Browser processes' echo 'indexb.label Index Browser processes' echo 'imap.label IMAP Server processes' diff --git a/plugins/scalix/scalix_queues b/plugins/scalix/scalix_queues index 00394b49..50ea7607 100755 --- a/plugins/scalix/scalix_queues +++ b/plugins/scalix/scalix_queues @@ -14,7 +14,7 @@ if [ "$1" = "config" ]; then echo 'graph_title Scalix queues' echo 'graph_args --lower-limit 0' echo 'graph_vlabel number' - echo 'graph_category Scalix' + echo 'graph_category mail' echo 'archive.label Archive' echo 'bb.label Bulletin Board' diff --git a/plugins/security/example-graphs/fail2ban_basic_jail-day.png b/plugins/security/example-graphs/fail2ban_-1.png similarity index 100% rename from plugins/security/example-graphs/fail2ban_basic_jail-day.png rename to plugins/security/example-graphs/fail2ban_-1.png diff --git a/plugins/security/example-graphs/fail2ban_cymru_asn-day.png b/plugins/security/example-graphs/fail2ban_-2.png similarity index 100% rename from plugins/security/example-graphs/fail2ban_cymru_asn-day.png rename to plugins/security/example-graphs/fail2ban_-2.png diff --git a/plugins/security/example-graphs/fail2ban_cymru_country-day.png b/plugins/security/example-graphs/fail2ban_-3.png similarity index 100% rename from plugins/security/example-graphs/fail2ban_cymru_country-day.png rename to plugins/security/example-graphs/fail2ban_-3.png diff --git a/plugins/security/example-graphs/fail2ban_cymru_rir-day.png b/plugins/security/example-graphs/fail2ban_-4.png similarity index 100% rename from plugins/security/example-graphs/fail2ban_cymru_rir-day.png rename to plugins/security/example-graphs/fail2ban_-4.png diff --git a/plugins/security/fail2ban_ b/plugins/security/fail2ban_ old mode 100644 new mode 100755 diff --git a/plugins/security/forefront_ b/plugins/security/forefront_ index ec6d94b5..951db439 100755 --- a/plugins/security/forefront_ +++ b/plugins/security/forefront_ @@ -27,7 +27,7 @@ # Config variables # # dsn - If DSN name differs from hostname -# dbuser - Valid MS SQL user (Windows authentication is posible using "DOMAIN\user") +# dbuser - Valid MS SQL user (Windows authentication is possible using "DOMAIN\user") # dbpass - Password # # Install guide: diff --git a/plugins/senderbase/senderbase b/plugins/senderbase/senderbase new file mode 100755 index 00000000..88da9590 --- /dev/null +++ b/plugins/senderbase/senderbase @@ -0,0 +1,93 @@ +#!/bin/sh + +: << =cut + +=head1 NAME + +senderbase - Gives the current SenderBase reputation for a mail host. + +SenderBase is a mail server reputation service run by Cisco. It's +basically a measure of how likely or unlikely they think it is for the +server in question to be sending spam, rated on a scale of -10 (near-100% +chance of spam) to 10 (near-0% chance of spam). The SenderBase score for +a server is one of several criteria that may be used when doing spam +filtering; consequently, it's useful for the operator of a mail server to +track their current reputation score. + +=head1 CONFIGURATION + +By default, the script will use the first IP address returned by +C. If that's not the right address to use, set the +C environment variable to the appropriate value. (Note that +if a host has multiple IP addresses, there's no guaranteed ordering for +C, so you should set the address explicitly in that case.) + + [senderbase] + env.ip_address 8.8.8.8 + +=head1 AUTHOR + +Phil! Gold + +=head1 LICENSE + +This plugin is made available under a CC0 waiver: + + https://creativecommons.org/publicdomain/zero/1.0/ + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=cut + +if which dig >/dev/null; then + query_program=dig + do_query () { + hostname=$1 + dig +short $hostname TXT | sed 's/"//g' | head -1 + } +elif which host >/dev/null; then + query_program=host + do_query () { + hostname=$1 + host -t TXT $hostname | grep -m 1 'descriptive text' | cut -d\" -f2 + } +else + query_program= + do_query () { + true + } +fi + +real_ip_address="${ip_address:-$(hostname -I | awk '{print $1}')}" +ip_reversed="$(echo $real_ip_address | sed -re 's/^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$/\4.\3.\2.\1/')" +query_host="${ip_reversed}.rf.senderbase.org" + +if [ "$1" = "autoconf" ]; then + if [ -z "$query_program" ]; then + echo 'no (No "dig" or "host" executable found.)' + elif [ -z "$(do_query "$query_host")" ]; then + echo "no (No SenderBase reputation for IP address ${ip_address}.)" + else + echo 'yes' + fi + exit 0 +fi + +if [ "$1" = "config" ]; then + cat < 0) && ($ARGV[0] eq "config")) { print("graph_args --lower-limit 0\n"); print("graph_vlabel Ampere\n"); } - print("graph_category envsensors\n"); + print("graph_category sensors\n"); print("host_name $hostname\n"); } my $sensordata = XMLin($sensorxml, KeyAttr => { }, ForceArray => [ 'data' ] ); diff --git a/plugins/sensors/voltcraft_tcm220_ b/plugins/sensors/voltcraft_tcm220_ old mode 100644 new mode 100755 diff --git a/plugins/snmp/snmp__sfsnmp_fan b/plugins/sfsnmp/snmp__sfsnmp_fan similarity index 100% rename from plugins/snmp/snmp__sfsnmp_fan rename to plugins/sfsnmp/snmp__sfsnmp_fan diff --git a/plugins/snmp/snmp__sfsnmp_temp b/plugins/sfsnmp/snmp__sfsnmp_temp similarity index 100% rename from plugins/snmp/snmp__sfsnmp_temp rename to plugins/sfsnmp/snmp__sfsnmp_temp diff --git a/plugins/snmp/snmp__sfsnmp_volt b/plugins/sfsnmp/snmp__sfsnmp_volt similarity index 100% rename from plugins/snmp/snmp__sfsnmp_volt rename to plugins/sfsnmp/snmp__sfsnmp_volt diff --git a/plugins/sge/sge_job_stats b/plugins/sge/sge_job_stats new file mode 100755 index 00000000..fe2c1087 --- /dev/null +++ b/plugins/sge/sge_job_stats @@ -0,0 +1,64 @@ +#!/bin/sh + +# Graphs the number of running and waiting jobs, as well as when they +# are submitted. +# +# Tested with Son of Gridengine. + +: <<=cut + +=head1 NAME + +sge_job_stats - Munin plugin to monitor jobs submitted and executed on a Grid +Engine installation + +=head1 APPLICABLE SYSTEMS + +Any system with a qstat command that behaves like the one found in Son of +GridEngine. + +=head1 CONFIGURATION + +The environment variable SGE_SETTINGS can be set to point to an SGE +"settings.sh" file. It defaults to /opt/sge/default/common/settings.sh. + +=head1 AUTHOR + +Wouter Verhelst + +=cut + +if [ "$1" = "config" ] +then + echo graph_title SGE gridengine jobs + echo graph_vlabel count + echo graph_category htc + echo running.label Running jobs + echo running.type GAUGE + echo running.draw AREA + echo waiting.label Queued jobs + echo waiting.type GAUGE + echo waiting.draw STACK + echo maxnum.label Submitted jobs + echo maxnum.type DERIVE + echo maxnum.draw LINE2 + echo maxnum.min 0 + + exit 0 +fi + +SGE_SETTINGS=${SGE_SETTINGS:-/opt/sge/default/common/settings.sh} + +. $SGE_SETTINGS + +qstat -u '*' | awk ' + BEGIN{maxnum = 0; running = 0; waiting = 0} + /^ /{ + maxnum = (maxnum > $1 ? maxnum : $1); + if ( $5 == "r" ) { + running += 1; + } else { + waiting += 1; + } + } + END { printf("running.value %d\nwaiting.value %d\nmaxnum.value %d\n", running, waiting, maxnum); }' diff --git a/plugins/sge/sge_queue_ b/plugins/sge/sge_queue_ index ad036245..791ee9ae 100755 --- a/plugins/sge/sge_queue_ +++ b/plugins/sge/sge_queue_ @@ -58,7 +58,7 @@ if [ "$1" = "config" ]; then echo "graph_vlabel Queue slots" echo "graph_scale no" echo "graph_info Shows global Sun Grid Engine queue state." - echo "graph_category SGE" + echo "graph_category htc" echo "graph_period minute" echo "used.label Used" echo "used.draw AREA" diff --git a/plugins/sge/sge_queue_xml_ b/plugins/sge/sge_queue_xml_ old mode 100644 new mode 100755 index bc026914..b20d088f --- a/plugins/sge/sge_queue_xml_ +++ b/plugins/sge/sge_queue_xml_ @@ -56,7 +56,7 @@ if [ "$1" = "config" ]; then echo "graph_vlabel Queue slots" echo "graph_scale no" echo "graph_info Shows global Grid Engine queue state." - echo "graph_category SGE" + echo "graph_category htc" echo "graph_period minute" echo "used.label Used" echo "used.draw AREA" diff --git a/plugins/streaming/shoutcast b/plugins/shoutcast/shoutcast similarity index 98% rename from plugins/streaming/shoutcast rename to plugins/shoutcast/shoutcast index 4949d63f..386bf225 100755 --- a/plugins/streaming/shoutcast +++ b/plugins/shoutcast/shoutcast @@ -68,7 +68,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { # Default Settings print "graph_title SHOUTcast Online\n"; print "graph_args --base 1000\n"; - print "graph_category shoutcast\n"; + print "graph_category streaming\n"; print "graph_vlabel Connections per \${graph_period}\n"; # Max Listeners Allowed to Connect to Server diff --git a/plugins/streaming/shoutcast2_multi b/plugins/shoutcast/shoutcast2_multi similarity index 99% rename from plugins/streaming/shoutcast2_multi rename to plugins/shoutcast/shoutcast2_multi index 79ba142f..5f922ba1 100755 --- a/plugins/streaming/shoutcast2_multi +++ b/plugins/shoutcast/shoutcast2_multi @@ -78,7 +78,7 @@ $graphsRef->{active} = { config => { args => '--base 1000 --lower-limit 0', vlabel => 'Is a DJ Actively Connected?', - category => 'shoutcast2', + category => 'streaming', title => 'Active States', info => 'This graph shows us the active state or not, depending on if a DJ is connected', }, @@ -91,7 +91,7 @@ $graphsRef->{listeners} = { config => { args => '--base 1000 --lower-limit 0', vlabel => 'Listener Count', - category => 'shoutcast2', + category => 'streaming', title => 'Listeners', info => 'This graph shows us the various counts for listener states we are tracking', }, @@ -440,3 +440,5 @@ sub fetch_admin_data { } } +# for Munin Plugin Gallery +# graph_category streaming diff --git a/plugins/sickbeard/sickbeard_episodes b/plugins/sickbeard/sickbeard_episodes index 2a6eb7d5..8b297006 100755 --- a/plugins/sickbeard/sickbeard_episodes +++ b/plugins/sickbeard/sickbeard_episodes @@ -45,8 +45,9 @@ if(defined $ARGV[0] && $ARGV[0] eq 'config') print <jsonToObj($req->content()); if ($json->{result} eq 'success') { print "down.value $json->{data}->{ep_downloaded}\n"; + print "snatched.value $json->{data}->{ep_snatched}\n"; print "total.value $json->{data}->{ep_total}\n"; exit 0; } else { diff --git a/plugins/sickbeard/sickbeard_shows b/plugins/sickbeard/sickbeard_shows index a81c6bd8..9d334f50 100755 --- a/plugins/sickbeard/sickbeard_shows +++ b/plugins/sickbeard/sickbeard_shows @@ -45,7 +45,7 @@ if(defined $ARGV[0] && $ARGV[0] eq 'config') print < { title => 'Pages', vlabel => 'Printed pages', - category => 'Printer', + category => 'printing', info => 'Returns the total number of printed pages per defined printer', total => 'Total', units => 'pages' @@ -114,7 +114,7 @@ $oid = $ENV{snmp_oid} || $defaults{oid}; '1.3.6.1.2.1.43.11.1.1.8.1.1' => { title => 'Total projected capacity of this cartridge', vlabel => 'Total capacity', - category => 'Printer', + category => 'printing', info => 'Returns the total projected capacity (in pages) of '. 'the currently installed cartridge', total => 'Total', @@ -123,7 +123,7 @@ $oid = $ENV{snmp_oid} || $defaults{oid}; '1.3.6.1.2.1.43.11.1.1.9.1.1' => { title => 'Pages printed with this cartridge', vlabel => 'Printed pages', - category => 'Printer', + category => 'printing', info => 'Returns the total number of printed pages per ' . 'defined printer with the current cartridge', total => 'Total', @@ -239,3 +239,6 @@ sub host_label_for { $addr =~ s/\./_/g; # Periods not allowed in variable names return $addr; } + +# for Munin Plugin Gallery +# graph_category printing diff --git a/plugins/snmp/snmp__airport b/plugins/snmp/snmp__airport old mode 100644 new mode 100755 diff --git a/plugins/snmp/snmp__cpu_usage b/plugins/snmp/snmp__cpu_usage index 80b603b6..00aa86f2 100755 --- a/plugins/snmp/snmp__cpu_usage +++ b/plugins/snmp/snmp__cpu_usage @@ -140,7 +140,7 @@ if ($ARGV[0] and $ARGV[0] eq "config") print "graph_title CPU Usage on $host\n"; print "graph_args --base 1000 --lower-limit 0\n"; print "graph_vlabel Percentage on one minute\n"; - print "graph_category system\n"; + print "graph_category cpu\n"; print "graph_info This graph shows the CPU usage on one minute\n"; my $firstCounter = 1; diff --git a/plugins/snmp/snmp__cyberpower b/plugins/snmp/snmp__cyberpower new file mode 100755 index 00000000..8b3c9ee3 --- /dev/null +++ b/plugins/snmp/snmp__cyberpower @@ -0,0 +1,186 @@ +#!/usr/bin/perl -w + +=head1 NAME + +Monitor CyberPower UPS Battery Status. + + +=head1 AUTHOR + +Kai Boenke + +=head1 LICENSE + +Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0) + +=back + + +##### +# Enable SNMP-Discovery +### +=head1 MAGIC MARKERS + #%# family=snmpauto + #%# capabilities=snmpconf +=cut +if (defined $ARGV[0] and $ARGV[0] eq "snmpconf") { + print "require 1.3.6.1.2.1.33.1.2.4.0\n"; + exit 0; +} + + +##### +# Initialize +### +use strict; +use Munin::Plugin::SNMP; +my $session = Munin::Plugin::SNMP->session(); + + +##### +# Declare OIDs +### +use constant oid_cps_battery_runtime => ".1.3.6.1.2.1.33.1.2.3.0"; +use constant oid_cps_battery_charge => ".1.3.6.1.2.1.33.1.2.4.0"; +use constant oid_cps_input_voltage => ".1.3.6.1.2.1.33.1.3.3.1.3.1"; +use constant oid_cps_output_voltage => ".1.3.6.1.2.1.33.1.4.4.1.2.1"; +use constant oid_cps_output_load => ".1.3.6.1.2.1.33.1.4.4.1.5.1"; +use constant oid_cps_env_temp => ".1.3.6.1.4.1.3808.1.1.4.2.1.0"; +use constant oid_cps_env_humidity => ".1.3.6.1.4.1.3808.1.1.4.3.1.0"; + + +##### +# Config +### +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 "multigraph cyberpower_load +graph_title CyberPower UPS Status +graph_info This graph shows battery status information. +graph_category sensors +graph_vlabel % +graph_args --upper-limit 100 -l 0 +graph_scale no +"; + print "load.label Total load +load.draw AREA +load.type GAUGE +load.min 0 +load.max 100 +"; + print "charge.label Battery charge +charge.draw LINE1 +charge.type GAUGE +charge.min 0 +charge.max 100 +"; + + print "multigraph cyberpower_runtime +graph_title CyberPower UPS Runtime +graph_info This graph shows expected runtime informatiom. +graph_category sensors +graph_vlabel minutes +"; + print "runtime.label Expected runtime +runtime.draw AREA +runtime.type GAUGE +"; + + print "multigraph cyberpower_voltage +graph_title CyberPower UPS Voltages +graph_info This graph shows voltage information. +graph_category sensors +graph_vlabel V +"; + print "input.label Input voltage +input.draw LINE2 +input.type GAUGE +"; + print "output.label Output voltage +output.draw LINE1 +output.type GAUGE +"; + + if(oidExists(oid_cps_env_temp) && oidExists(oid_cps_env_humidity)){ + print "multigraph cyberpower_environment +graph_title CyberPower UPS Environment +graph_info This graph shows environmental status information. +graph_category sensors +graph_vlabel F/% +"; + print "temp.label Temperature +temp.draw LINE2 +temp.type GAUGE +"; + print "humidity.label Humidity +humidity.draw LINE1 +humidity.type GAUGE +humidity.min 0 +humidity.max 100 +"; + } + + exit 0; +} + + +##### +# Get Values +### + +print "multigraph cyberpower_load\n"; +my $load = $session->get_single(oid_cps_output_load); +my $charge = $session->get_single(oid_cps_battery_charge); +if($load ne 'U'){ + print "load.value ", $load, "\n"; +} +if($charge ne 'U'){ + print "charge.value ", $charge, "\n"; +} + +print "multigraph cyberpower_runtime\n"; +my $runtime = $session->get_single(oid_cps_battery_runtime); +if($runtime ne 'U'){ + print "runtime.value ", $runtime, "\n"; +} + +print "multigraph cyberpower_voltage\n"; +my $input = $session->get_single(oid_cps_input_voltage); +my $output = $session->get_single(oid_cps_output_voltage); +if($input ne 'U'){ + print "input.value ", $input, "\n"; +} +if($output ne 'U'){ + print "output.value ", $output, "\n"; +} + +if(oidExists(oid_cps_env_temp) && oidExists(oid_cps_env_humidity)){ + print "multigraph cyberpower_environment\n"; + my $temp = $session->get_single(oid_cps_env_temp); + my $humidity = $session->get_single(oid_cps_env_humidity); + if($temp ne 'U'){ + $temp /= 10; + print "temp.value ", $temp, "\n"; + } + if($humidity ne 'U'){ + print "humidity.value ", $humidity, "\n"; + } +} + +##### +# Subroutines +### +sub oidExists { + if(not defined $_[0]) { + exit 0; + } + my $oid = $_[0]; + my $val = $session->get_single($oid); + + if(!length $val || $val eq 'noSuchInstance' || $val eq 'U'){ + return(0); + }else{ + return(1); + } +} diff --git a/plugins/snmp/snmp__fn/snmp__fn b/plugins/snmp/snmp__fn similarity index 98% rename from plugins/snmp/snmp__fn/snmp__fn rename to plugins/snmp/snmp__fn index 1216e021..e6122725 100755 --- a/plugins/snmp/snmp__fn/snmp__fn +++ b/plugins/snmp/snmp__fn @@ -114,7 +114,7 @@ config() echo "multigraph fn_cpu" echo "host_name $SNMPCLIENT" echo "graph_title $UNIT - CPU usage" - echo 'graph_category system' + echo 'graph_category fw' echo 'graph_vlabel %' echo 'graph_info This graph shows current CPU usage.' echo 'graph_args --base 1000 -r --lower-limit 0 --upper-limit 100' @@ -125,7 +125,7 @@ config() echo "multigraph fn_memory" echo "host_name $SNMPCLIENT" echo "graph_title $UNIT - Memory usage" - echo 'graph_category system' + echo 'graph_category fw' echo 'graph_vlabel %' echo 'graph_info This graph shows current memory usage.' echo 'graph_args --base 1000 -r --lower-limit 0 --upper-limit 100' @@ -136,7 +136,7 @@ config() echo "multigraph fn_sessions" echo "host_name $SNMPCLIENT" echo "graph_title $UNIT - Sessions" - echo 'graph_category Other' + echo 'graph_category fw' echo 'graph_vlabel Active Sessions' echo 'graph_info Active session count on the Fortigate firewall' echo 'fortisessions.label Sessions' @@ -146,7 +146,7 @@ config() echo "multigraph fn_vpnsessions" echo "host_name $SNMPCLIENT" echo "graph_title $UNIT - SSLvpn Sessions" - echo 'graph_category Other' + echo 'graph_category fw' echo 'graph_vlabel Sessions/Users' echo 'graph_info Loged in users with SSLvpn (WebSession or Tunnel-Mode)' echo 'fortiuser.label Users' diff --git a/plugins/snmp/snmp__if_combined b/plugins/snmp/snmp__if_combined index dbe141f2..ddb52219 100755 --- a/plugins/snmp/snmp__if_combined +++ b/plugins/snmp/snmp__if_combined @@ -47,7 +47,7 @@ It may show something like this: IF-MIB::ifType.123 = INTEGER: l2vlan(135) propVirtual or l2vlan is usually used for VLAN interfaces. Tunnel -would normaly be for VPNs. A minor horde of different interface types +would normally be for VPNs. A minor horde of different interface types are supposted, please see IANAifType-MIB (on your system or find with Google) for a full list. diff --git a/plugins/snmp/snmp__memory_openwrt b/plugins/snmp/snmp__memory_openwrt deleted file mode 100755 index 736af06a..00000000 --- a/plugins/snmp/snmp__memory_openwrt +++ /dev/null @@ -1,109 +0,0 @@ -#!/usr/bin/perl -w -# -# Copyright (C) 2008 J.M.Roth -# -# 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. -# -# -# $Log$ -# -# Inspired by snmp__processes -# -#%# family=snmpauto -#%# capabilities=snmpconf - -use strict; -use Net::SNMP; - -my $DEBUG = 0; - -my $host = $ENV{host} || undef; -my $port = $ENV{port} || 161; -my $community = $ENV{community} || "public"; -my $iface = $ENV{interface} || undef; - -my $response; - -if (defined $ARGV[0] and $ARGV[0] eq "snmpconf") -{ - print "require 1.3.6.1.2.1.25.2.3.1.5.2\n"; # Number - print "require 1.3.6.1.2.1.25.2.3.1.6.2\n"; # Number - exit 0; -} - -my @split_result = split(/_/, $0); -$host = $split_result[1]; -if ($host =~ /^([^:]+):(\d+)$/) -{ - $host = $1; - $port = $2; -} -if ($host eq '' || !defined $host) -{ - print "# Debug: $0 -- $1\n" if $DEBUG; - die "# Error: couldn't understand what I'm supposed to monitor."; -} - -my ($session, $error) = Net::SNMP->session( - -hostname => $host, - -community => $community, - -port => $port - ); - -if (!defined ($session)) -{ - die "Croaking: $error"; -} - -if (defined $ARGV[0] and $ARGV[0] eq "config") -{ - print "host_name $host\n"; - print "graph_title Memory usage -graph_args --base 1000 -l 0 -graph_vlabel kB -graph_category system -graph_info This graph shows total and used memory on the host. -memsize.label total -memused.label used -memsize.info The total memory. -memused.info The memory in use. -memsize.draw LINE1 -memused.draw LINE2 -"; - - exit 0; -} - -print "memsize.value ", &get_single ($session, "1.3.6.1.2.1.25.2.3.1.5.2"), "\n"; -print "memused.value ", &get_single ($session, "1.3.6.1.2.1.25.2.3.1.6.2"), "\n"; - -sub get_single -{ - my $handle = shift; - my $oid = shift; - - print "# Getting single $oid...\n" if $DEBUG; - - $response = $handle->get_request ($oid); - - if (!defined $response->{$oid}) - { - return undef; - } - else - { - return $response->{$oid}; - } -} diff --git a/plugins/snmp/snmp__rms2_ b/plugins/snmp/snmp__rms2_ index db369752..66309be6 100755 --- a/plugins/snmp/snmp__rms2_ +++ b/plugins/snmp/snmp__rms2_ @@ -185,7 +185,7 @@ if ($ARGV[0] and $ARGV[0] eq "config") { print("host_name $host\n"); printf("graph_title %s\n", $graphs->{$mode}->{'title'}); # print "graph_args --base 1000\n"; - print("graph_category envsensor\n"); + print("graph_category sensors\n"); printf("graph_vlabel %s\n", $graphs->{$mode}->{'unit'}); unless (defined($graphs->{$mode}->{'thatype'})) { print("graph_info For contact sensors, the values mean: 1 = open, 2 = closed, 3 = armed, 4 = triggered\n"); diff --git a/plugins/snmp/snmp__webthermometer b/plugins/snmp/snmp__webthermometer index c81ac19f..34a2a827 100755 --- a/plugins/snmp/snmp__webthermometer +++ b/plugins/snmp/snmp__webthermometer @@ -59,7 +59,7 @@ if [ "$1" = "config" ]; then # some fix values echo "host_name $SNMPCLIENT" - echo 'graph_category Other' + echo 'graph_category sensors' echo 'graph_args --base 1000 -l 0' # some variables, fetched from the device diff --git a/plugins/solaris/forks b/plugins/solaris/forks new file mode 100755 index 00000000..1744bdf3 --- /dev/null +++ b/plugins/solaris/forks @@ -0,0 +1,108 @@ +#!/bin/bash + +: << =cut + +=head1 NAME + + forks - Munin plugin to monitor Solaris fork and exec rate + +=head1 CONFIGURATION + + Make symlink: + cd /path/to/munin/etc/plugins + ln -s /path/to/munin/lib/plugins/forks . + +=head1 AUTHOR + + K.Cima https://github.com/shakemid + +=head1 LICENSE + + GPLv2 + +=head1 Magic markers + + #%# family=contrib + #%# capabilities=autoconf + +=cut + +# Include plugin.sh +. "${MUNIN_LIBDIR:-}/plugins/plugin.sh" + +# Shell options +set -o nounset # Like perl use strict; + +# Graph settings +global_attr=" + graph_title Fork and exec rate + graph_category processes + graph_args --base 1000 --lower-limit 0 --rigid + graph_vlabel count per second + graph_info Fork and exec rate +" +# data_attr format: field type draw label +# label can contain white-spaces. +data_attr=" + sysfork DERIVE LINE fork + sysvfork DERIVE LINE vfork + sysexec DERIVE LINE exec +" + +# Functions + +autoconf() { + if which kstat >/dev/null ; then + echo yes + else + echo "no (failed to find executable 'kstat')" + fi +} + +config() { + local label_max_length=45 + + # print global attributes + echo "$global_attr" | sed -e 's/^ *//' -e '/^$/d' + + # print data source attributes + # split line into field,type,draw,label + local field type draw label + echo "$data_attr" | while read -r field type draw label + do + [ -z "$field" ] && continue + + echo "${field}.type ${type}" + echo "${field}.draw ${draw}" + echo "${field}.label ${label:0:${label_max_length}}" + if [ "${type}" = DERIVE ]; then + echo "${field}.min 0" + fi + done +} + +fetch() { + local field type draw label + echo "$data_attr" | while read -r field type draw label + do + [ -z "$field" ] && continue + value=$( kstat -p "cpu::sys:${field}" | awk '{ sum += $2 } END { print sum }' ) + echo "${field}.value ${value}" + done +} + +# Main +case ${1:-} in +autoconf) + autoconf + ;; +config) + config + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = "1" ]; then fetch; fi + ;; +*) + fetch + ;; +esac + +exit 0 diff --git a/plugins/solaris/fsstat_act_ b/plugins/solaris/fsstat_act_ new file mode 100755 index 00000000..3332e0a8 --- /dev/null +++ b/plugins/solaris/fsstat_act_ @@ -0,0 +1,173 @@ +#!/bin/bash + +: << =cut + +=head1 NAME + + fsstat_act_ - Munin wildcard plugin to monitor Solaris file system statistics + + This plugin monitors full activity of file system like fsstat -f . + See man fsstat for more details. + + Tested with Solaris 10 and 11. And should work with Solaris 10 11/06 or above. + + Note: + In Solaris 11, fsstat command can get stats for each non-global zones in + global zone. (see man fsstat) + In global zone, this plugin gets stats of only global zone. + In non-global zones, this plugin reports stats of the non-global zones. + +=head1 CONFIGURATION + + This plugin monitors statistics per file system type. Available file system + can be shown by fsstat command. + + Make symlink: + cd /path/to/munin/etc/plugins + ln -s /path/to/munin/lib/plugins/fsstat_act_ fsstat_act_zfs + ln -s /path/to/munin/lib/plugins/fsstat_act_ fsstat_act_nfs3 + ln -s /path/to/munin/lib/plugins/fsstat_act_ fsstat_act_nfs4 + ... + +=head1 ENVIRONMENT VARIABLES + + None + +=head1 AUTHOR + + K.Cima https://github.com/shakemid + +=head1 LICENSE + + GPLv2 + +=head1 Magic markers + + #%# family=contrib + #%# capabilities=autoconf suggest + +=cut + +# Include plugin.sh +. "${MUNIN_LIBDIR:-}/plugins/plugin.sh" + +# Shell options +set -o nounset # Like perl use strict; + +# Set environment variables +plugin_name=${0##*/} +fs_type=${plugin_name##*_} +name_regexp='/^vopstats_(?![0-9a-f]{7})[a-z]/' # data source of fsstat +stat_regexp='/^(?!(class|crtime|snaptime|.*_bytes))/' + +# Graph settings +global_attr=" + graph_title File system statictics - Activities of $( echo "$fs_type" | tr '[:lower:]' '[:upper:]' ) + graph_category disk + graph_args --base 1000 + graph_vlabel Counts per second + graph_info File system statictics - Activities of $( echo "$fs_type" | tr '[:lower:]' '[:upper:]' ) +" + +# Functions + +get_zone_id() { + local osver zonename zoneid + + # Note: Solaris 11 fsstat supports statistics per zone. Solaris 10 does not. + + zoneid=0 + osver=$( uname -r | cut -d. -f2 ) + + if [ "$osver" -ge 11 ]; then + zonename=$( zonename ) + zoneid=$( /usr/sbin/zoneadm list -p | awk -F: '$2 == "'"$zonename"'" { print $1 }' ) + fi + + echo "$zoneid" +} + +autoconf() { + if which kstat >/dev/null ; then + echo yes + else + echo "no (failed to find executable 'kstat')" + fi +} + +suggest() { + # Print file systems which look active + + kstat -p "unix:${zone_id}:${name_regexp}:/^(read_bytes|write_bytes)\$/" \ + | sed -e 's/vopstats_//' -e 's/:/ /g' \ + | awk '{ + sum[ $3 ] += $5 + } + END { + for ( i in sum ) { + if ( sum[i] != 0 ) { + print i + } + } + }' | sort +} + +config() { + local stat + + # Print global attributes + echo "$global_attr" | sed -e 's/^ *//' -e '/^$/d' + + # Get stat names by kstat + kstat -p "unix:${zone_id}:vopstats_${fs_type}:${stat_regexp}" \ + | sed -e 's/vopstats_//' -e 's/:/ /g' | awk '{ print $4 }' | sort \ + | while read -r stat + do + # Print data attributes + echo "${stat}.label ${stat#n}" + echo "${stat}.graph line" + echo "${stat}.type DERIVE" + echo "${stat}.min 0" + done +} + +fetch() { + local stat value + + # Get fs names, stat names and values by kstat + + # kstat output example: + # $ kstat -p 'unix::/^vopstats_[a-z]/:nread' + # unix:0:vopstats_autofs:nread 2 + # unix:0:vopstats_hsfs:nread 407790 + # ... + + kstat -p "unix:${zone_id}:vopstats_${fs_type}:${stat_regexp}" \ + | sed -e 's/vopstats_//' -e 's/:/ /g' | awk '{ print $4,$5 }' \ + | while read -r stat value + do + echo "${stat}.value ${value}" + done +} + +# Main + +zone_id=$( get_zone_id ) + +case ${1:-} in +autoconf) + autoconf + ;; +suggest) + suggest + ;; +config) + config + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = "1" ]; then fetch; fi + ;; +*) + fetch + ;; +esac + +exit 0 diff --git a/plugins/solaris/fsstat_bytes b/plugins/solaris/fsstat_bytes new file mode 100755 index 00000000..4fb5e7e1 --- /dev/null +++ b/plugins/solaris/fsstat_bytes @@ -0,0 +1,170 @@ +#!/bin/bash + +: << =cut + +=head1 NAME + + fsstat_bytes - Munin plugin to monitor Solaris file system statistics + + Tested with Solaris 10 and 11. + + Note: + In Solaris 11, fsstat command can get stats for each non-global zones in + global zone. (see man fsstat) + In global zone, this plugin gets stats of only global zone. + In non-global zones, this plugin reports stats of the non-global zones. + +=head1 CONFIGURATION + + Make symlink: + cd /path/to/munin/etc/plugins + ln -s /path/to/munin/lib/plugins/fsstat_bytes . + +=head1 ENVIRONMENT VARIABLES + + env.exclude - file system(s) to exclude seperated by white-space. + example: env.exclude autofs + default: none + +=head1 AUTHOR + + K.Cima https://github.com/shakemid + +=head1 LICENSE + + GPLv2 + +=head1 Magic markers + + #%# family=contrib + #%# capabilities=autoconf + +=cut + +# Include plugin.sh +. "${MUNIN_LIBDIR:-}/plugins/plugin.sh" + +# Shell options +set -o nounset # Like perl use strict; + +# Set environment variables +name_regexp='/^vopstats_(?![0-9a-f]{7})[a-z]/' # data source of fsstat +: "${exclude:=}" + +# Set graph settings +global_attr=" + graph_title File system statictics - I/O throughput + graph_category disk + graph_args --base 1024 + graph_vlabel Bytes per second write (-) / read (+) + graph_info File system statictics - I/O throughput +" +data_in=read_bytes +data_out=write_bytes + + +# Functions + +is_excluded() { + local arg i + arg=$1 + + for i in ${exclude} + do + if [ "$arg" = "$i" ]; then + return 0 + fi + done + + return 1 +} + +get_zone_id() { + local osver zonename zoneid + + # Note: Solaris 11 fsstat supports statistics per zone. Solaris 10 does not. + + zoneid=0 + osver=$( uname -r | cut -d. -f2 ) + + if [ "$osver" -ge 11 ]; then + zonename=$( zonename ) + zoneid=$( /usr/sbin/zoneadm list -p | awk -F: '$2 == "'"$zonename"'" { print $1 }' ) + fi + + echo "$zoneid" +} + +autoconf() { + if which kstat >/dev/null ; then + echo yes + else + echo "no (failed to find executable 'kstat')" + fi +} + +config() { + local fs + + # Print global attributes + echo "$global_attr" | sed -e 's/^ *//' -e '/^$/d' + + # Get fs names by kstat + kstat -p "unix:${zone_id}:${name_regexp}:${data_in}" \ + | sed -e 's/vopstats_//' -e 's/:/ /g' | awk '{ print $3 }' | sort \ + | while read -r fs + do + is_excluded "$fs" && continue + + # Print data attributes + echo "${fs}_${data_out}.label dummy" + echo "${fs}_${data_out}.graph no" + echo "${fs}_${data_out}.type DERIVE" + echo "${fs}_${data_out}.min 0" + + echo "${fs}_${data_in}.label ${fs}" + echo "${fs}_${data_in}.negative ${fs}_${data_out}" + echo "${fs}_${data_in}.type DERIVE" + echo "${fs}_${data_in}.min 0" + done +} + +fetch() { + local fs stat value + + # Get fs names, stat names and values by kstat + + # kstat output example: + # $ kstat -p 'unix::/^vopstats_[a-z]/:nread' + # unix:0:vopstats_autofs:nread 2 + # unix:0:vopstats_hsfs:nread 407790 + # ... + + kstat -p "unix:${zone_id}:${name_regexp}:/^(${data_in}|${data_out})\$/" \ + | sed -e 's/vopstats_//' -e 's/:/ /g' | awk '{ print $3,$4,$5 }' \ + | while read -r fs stat value + do + is_excluded "$fs" && continue + + echo "${fs}_${stat}.value ${value}" + done +} + +# Main + +zone_id=$( get_zone_id ) + +case ${1:-} in +autoconf) + autoconf + ;; +config) + config + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = "1" ]; then fetch; fi + ;; +*) + fetch + ;; +esac + +exit 0 diff --git a/plugins/solaris/interrupts b/plugins/solaris/interrupts new file mode 100755 index 00000000..7f1f49cc --- /dev/null +++ b/plugins/solaris/interrupts @@ -0,0 +1,128 @@ +#!/bin/bash + +: << =cut + +=head1 NAME + + interrupts - Munin plugin to monitor Solaris cpu interrupts and context switches + +=head1 CONFIGURATION + + Make symlink: + cd /path/to/munin/etc/plugins + ln -s /path/to/munin/lib/plugins/interrupts . + +=head1 AUTHOR + + K.Cima https://github.com/shakemid + +=head1 LICENSE + + GPLv2 + +=head1 Magic markers + + #%# family=contrib + #%# capabilities=autoconf + +=cut + +# Include plugin.sh +. "${MUNIN_LIBDIR:-}/plugins/plugin.sh" + +# Shell options +set -o nounset # Like perl use strict; + +# Graph settings +global_attr=" + graph_title Interrupts and context switches + graph_category system + graph_args --base 1000 --lower-limit 0 --rigid + graph_vlabel count per second + graph_info Interrupts and context switches + + rw_wrfails.graph no + rw_rdfails.cdef rw_rdfails,rw_wrfails,+ +" +# data_attr format: field type draw label +# label can contain white-spaces. +data_attr=" + intr DERIVE LINE interrupts + intrthread DERIVE LINE interrupts as threads + pswitch DERIVE LINE context switches + inv_swtch DERIVE LINE involuntary context switches + cpumigrate DERIVE LINE thread migrations + xcalls DERIVE LINE inter-processor cross-calls + mutex_adenters DERIVE LINE spins on mutexes + rw_rdfails DERIVE LINE spins on r/w locks + rw_wrfails DERIVE LINE dummy + syscall DERIVE LINE system calls + trap DERIVE LINE traps +" +# They can be shown by vmstat -s or mpstat + +# Functions + +autoconf() { + if which kstat >/dev/null ; then + echo yes + else + echo "no (failed to find executable 'kstat')" + fi +} + +config() { + local label_max_length=45 + + # print global attributes + echo "$global_attr" | sed -e 's/^ *//' -e '/^$/d' + + # print data source attributes + # split line into field,type,draw,label + local field type draw label + echo "$data_attr" | while read -r field type draw label + do + [ -z "$field" ] && continue + + echo "${field}.type ${type}" + echo "${field}.draw ${draw}" + echo "${field}.label ${label:0:${label_max_length}}" + if [ "${type}" = DERIVE ]; then + echo "${field}.min 0" + fi + done +} + +fetch() { + local field type draw label + echo "$data_attr" | while read -r field type draw label + do + [ -z "$field" ] && continue + + # kstat output example: + # $ kstat -p cpu::sys:intr + # cpu:0:sys:intr 5728473528 + # cpu:1:sys:intr 1060016014 + # cpu:2:sys:intr 1297441213 + # ... + value=$( kstat -p "cpu::sys:${field}" | awk '{ sum += $2 } END { print sum }' ) + + echo "${field}.value ${value}" + done +} + +# Main +case ${1:-} in +autoconf) + autoconf + ;; +config) + config + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = "1" ]; then fetch; fi + ;; +*) + fetch + ;; +esac + +exit 0 diff --git a/plugins/solaris/io_disk b/plugins/solaris/io_disk new file mode 100755 index 00000000..34ec68f0 --- /dev/null +++ b/plugins/solaris/io_disk @@ -0,0 +1,344 @@ +#!/bin/bash + +: << =cut + +=head1 NAME + + io_disk - Multigraph plugin to monitor disks for Solaris. + + These functions are implemented: + ops : similar to iostat r/s, w/s + bytes : similar to iostat kr/s, kw/s + busy : similar to iostat %b, %w (%w usually indicates 0 in Sol10,11) + queue : similar to iostat actv, wait + latency : similar to iostat asvc_t, wsvc_t + size : average io size + + This plugin is improved from Solaris io_busy_, io_ops_, io_bytes_ plugins. + Any device found in /usr/bin/kstat can be monitored. + + Tested with Solaris 10 and 11, and it should work with Solaris 8, 9. + +=head1 CONFIGURATION + + Make symlink: + cd /path/to/munin/etc/plugins + ln -s /path/to/munin/lib/plugins/io_disk . + + The RRD files generated by io_busy_, io_ops_, io_bytes_ can be taken over + by this plugin. + Thus, please remove symlinks of those plugins before using this plugin. + + By default, this plugin monitors disk devices. And also it can monitor + NFS and Tape devices as much as io_* plugins with setting environments. + + Note that instance names of nfs (e.g. nfs1) can be changed after reboot or + remount, this plugin may not monitor nfs properly. (same as original ones) + +=head1 ENVIRONMENT VARIABLES + + env.class - kstat class. See man kstat -c option. + example: env.class /disk|nfs|tape/ + default: disk + + env.module - Module name. Only used in internal graph name. + example: env.module something + default: sd + + If you would not like to take over RRDs generated by io_*_sd plugins, + please change it to another name. + + And also, if you would like to take over RRDs of nfs, tape devices, + please set it 'nfs' or 'tape' (and set env.class, env.module_regex). + It may be good to link like ln -s /path/to/io_disk io_nfs if necessary. + + env.module_regex - kstat module. See man kstat -m option. + example: env.module_regex /^(s?sd|vdc|zvblk|dad|md|nfs|st)$/ + default: /^(s?sd|vdc|zvblk|dad|md)$/ + + env.title_type - Device type shown in graph title. + example: env.title_type Disk Device, NFS, Tape + default: Disk Device + + env.exclude - Device instance name(s) to exclude seperated by white-space. + example: env.exclude sd0 ssd1 + default: none + + env.graph_width - Graph width in pixel + example: env.graph_width 450 + default: none (usually 400) + +=head1 AUTHOR + + Unknown Author + Improved by K.Cima https://github.com/shakemid + +=head1 LICENSE + + GPLv2 + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=cut + +# Include plugin.sh +. "${MUNIN_LIBDIR:-}/plugins/plugin.sh" +is_multigraph "$@" + +# Shell options +set -o nounset # Like perl use strict; + +# Set environment variables +plugin_name=io +functions='ops bytes busy queue latency size' +: "${class:=disk}" +: "${module:=sd}" +: "${module_regex:=/^(s?sd|vdc|zvblk|dad|md)\$/}" +: "${title_type:=Disk Device}" +: "${exclude:=}" +: "${graph_width:=}" + +# Create map of instance name (e.g. sd0) and logical device name (e.g. c0t0d0) +# Example: +# name_sd1=c0t0d0 +# name_ssd2=c0tAB_1234d0 (shorten long target) +# ... +instance_names=$( iostat -x | sed -e '1,2d' | awk '{print $1}' \ + | sed -e 's/^/name_/' ) +logical_device_names=$( iostat -xn | sed -e '1,2d' | awk '{print $NF}' \ + | sed -e 's/^\(c[0-9]*\)\(t.\{2\}\).*\(.\{4\}\)\(d[0-9]*\)$/\1\2_\3\4/' ) +declare $( paste -d= <( echo "$instance_names" ) <( echo "$logical_device_names" ) ) + +# Functions + +preconfig() { + local func + func=$1 + + case "$func" in + ops) + conf_title='I/O Operations' + conf_gargs='--base 1000' + conf_vlabel='Ops per second write (-) / read (+)' + conf_in=reads + conf_out=writes + ;; + bytes) + conf_title='I/O Throughput' + conf_gargs='--base 1024' + conf_vlabel='Bytes per second write (-) / read (+)' + conf_in=nread + conf_out=nwritten + ;; + busy) + conf_title='Busy & Wait' + conf_gargs='--base 1000 --lower-limit 0 --upper-limit 100' + conf_vlabel='% wait (-) / busy (+)' + conf_in=rtime + conf_out=wtime + ;; + queue) + conf_title='Queue Length' + conf_gargs='--base 1000' + conf_vlabel='Queue length wait (-) / actv (+)' + conf_in=rlentime + conf_out=wlentime + ;; + latency) + conf_title='Latency' + conf_gargs='--base 1000' + conf_vlabel='Seconds wsvc_t (-) / asvc_t (+)' + conf_in=rlentime + conf_out=wlentime + ;; + size) + conf_title='I/O Size' + conf_gargs='--base 1024' + conf_vlabel='Average size write (-) / read (+)' + conf_in=nread + conf_out=nwritten + ;; + *) + echo "Unknown function: $func" + exit 1 + ;; + esac +} + +is_excluded() { + local arg i + arg=$1 + + for i in ${exclude} + do + if [ "$arg" = "$i" ]; then + return 0 + fi + done + + return 1 +} + +do_config() { + local func dev ref devname stat + + func=$1 + preconfig "$func" + echo "multigraph ${plugin_name}_${func}_${module}" + + echo "graph_title $title_type $conf_title" + echo 'graph_category disk' + echo "graph_args $conf_gargs" + echo "graph_vlabel $conf_vlabel" + if [ -n "$graph_width" ]; then + echo "graph_width $graph_width" + fi + + # Get device instance names by kstat + kstat -p -c "$class" -m "$module_regex" -s "/^${conf_in}\$/" \ + | sed 's/:/ /g' | awk '{ print $3 }' \ + | while read -r dev + do + is_excluded "$dev" && continue + + # Replace instance name to logical device name if exists + ref="name_$dev" + devname=${!ref:-} # ${!varname} means indirect evaluation (similar to eval) + + # reads and writes are necessary to calculate latency, size + case "$func" in + latency|size) + for stat in reads writes + do + echo "${dev}_${stat}.label dummy" + echo "${dev}_${stat}.graph no" + echo "${dev}_${stat}.type DERIVE" + echo "${dev}_${stat}.min 0" + done + ;; + esac + + # Set CDEF + case "$func" in + busy) + conf_in_cdef="${dev}_${conf_in},100,*" + conf_out_cdef="${dev}_${conf_out},100,*" + ;; + latency) + # rlentime / ( reads + writes ) + conf_in_cdef="${dev}_${conf_in},${dev}_reads,${dev}_writes,+,/" + conf_out_cdef="${dev}_${conf_out},${dev}_reads,${dev}_writes,+,/" + ;; + size) + conf_in_cdef="${dev}_${conf_in},${dev}_reads,/" + conf_out_cdef="${dev}_${conf_out},${dev}_writes,/" + ;; + *) + conf_in_cdef= + conf_out_cdef= + ;; + esac + + # Print data attributes + echo "${dev}_${conf_out}.label dummy" + echo "${dev}_${conf_out}.graph no" + echo "${dev}_${conf_out}.type DERIVE" + echo "${dev}_${conf_out}.min 0" + if [ -n "$conf_out_cdef" ]; then + echo "${dev}_${conf_out}.cdef ${conf_out_cdef}" + fi + + echo "${dev}_${conf_in}.label ${devname:-${dev}}" + echo "${dev}_${conf_in}.negative ${dev}_${conf_out}" + echo "${dev}_${conf_in}.type DERIVE" + echo "${dev}_${conf_in}.min 0" + if [ -n "$conf_in_cdef" ]; then + echo "${dev}_${conf_in}.cdef ${conf_in_cdef}" + fi + done + + echo +} + +do_fetch() { + local func stat_regex dev stat value + + func=$1 + preconfig "$func" + echo "multigraph ${plugin_name}_${func}_${module}" + + case "$func" in + latency|size) + stat_regex="/^($conf_in|$conf_out|reads|writes)\$/" + ;; + *) + stat_regex="/^($conf_in|$conf_out)\$/" + ;; + esac + + # Get device instance names, stat names and values by kstat + + # kstat output example: + # $ kstat -p -c disk -m '/^sd$/' -s '/^reads$/' + # sd:0:sd0:reads 51432610 + # sd:1:sd1:reads 52671435 + # ... + + kstat -p -c "$class" -m "$module_regex" -s "$stat_regex" \ + | sed 's/:/ /g' | awk '{ print $3,$4,$5 }' \ + | while read -r dev stat value + do + is_excluded "$dev" && continue + + echo "${dev}_${stat}.value ${value%.*}" + done + + echo +} + +autoconf() { + if [ -x /usr/bin/kstat ]; then + echo yes + exit 0 + else + echo "no (/usr/bin/kstat not found)" + exit 0 + fi +} + +config() { + local func + + for func in $functions + do + do_config "$func" + done +} + +fetch() { + local func + + for func in $functions + do + do_fetch "$func" + done +} + +# Main +case ${1:-} in +autoconf) + autoconf + ;; +config) + config + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = "1" ]; then fetch; fi + ;; +*) + fetch + ;; +esac + +exit 0 diff --git a/plugins/system/solaris-memstat b/plugins/solaris/solaris-memstat similarity index 98% rename from plugins/system/solaris-memstat rename to plugins/solaris/solaris-memstat index ba818ec9..71957f53 100755 --- a/plugins/system/solaris-memstat +++ b/plugins/solaris/solaris-memstat @@ -52,7 +52,7 @@ if [ "$1" = "config" ]; then echo ' graph_args --base 1024 -l 0 --vertical-label Bytes graph_title Memory usage -graph_category system +graph_category memory graph_info This graph shows system memory use. graph_order kernel anon exec cacheused zfs cachefree free @@ -89,7 +89,7 @@ fi echo "::memstat" | mdb -k | nawk ' BEGIN { - pagesize='$(getconf PAGESIZE)' + pagesize='"$(getconf PAGESIZE)"' kernel=0 zfs=0 anon=0 diff --git a/plugins/system/zones_cpu b/plugins/solaris/zones_cpu similarity index 100% rename from plugins/system/zones_cpu rename to plugins/solaris/zones_cpu diff --git a/plugins/system/zones_mem b/plugins/solaris/zones_mem similarity index 97% rename from plugins/system/zones_mem rename to plugins/solaris/zones_mem index 9d216065..d492af05 100755 --- a/plugins/system/zones_mem +++ b/plugins/solaris/zones_mem @@ -26,7 +26,7 @@ fi if [ "$1" = 'config' ]; then echo 'graph_title zone memory usage' echo 'graph_args --upper-limit 100' - echo 'graph_category system' + echo 'graph_category memory' stack=AREA $PRSTAT $PRSTAT_OPTS | sed '1,/^ZONEID/d' | grep -v '^Total' | while read i; do oIFS="$IFS" diff --git a/plugins/solr/solr b/plugins/solr/solr index f243d74e..647ea5e9 100755 --- a/plugins/solr/solr +++ b/plugins/solr/solr @@ -88,7 +88,7 @@ if len(sys.argv) > 1: print 'graph_title Solr %s' % params['valueName'] print 'graph_args -l 0 ' print 'graph_vlabel Size %s' % params['valueName'] - print 'graph_category Solr' + print 'graph_category search' print 'graph_info Info for cores: %s' % ( ",".join(params['cores'])) for core in params['cores']: diff --git a/plugins/solr/solr-stats b/plugins/solr/solr-stats index 9c3d84a6..e023d339 100755 --- a/plugins/solr/solr-stats +++ b/plugins/solr/solr-stats @@ -24,7 +24,7 @@ $property = $tabParams[4]; if('config' === $action) { - echo 'graph_category SolR ' . $core . "\n"; + echo 'graph_category search ' . $core . "\n"; echo 'graph_title ' . $item . ' ' . $property . "\n"; echo 'graph_vlabel ' . $property . "\n"; echo $core . $item . $property . 'solr.label ' . $property . "\n"; diff --git a/plugins/solr/solr4_ b/plugins/solr/solr4_ index 81b67430..247c0810 100755 --- a/plugins/solr/solr4_ +++ b/plugins/solr/solr4_ @@ -232,7 +232,7 @@ class SolrCoreMBean: # Graph Templates CACHE_GRAPH_TPL = """multigraph solr_{core}_{cacheType}_hit_rates -graph_category solr +graph_category search graph_title Solr {core} {cacheName} Hit rates graph_order lookups hits inserts graph_scale no @@ -256,7 +256,7 @@ hits.type DERIVE multigraph solr_{core}_{cacheType}_size graph_title Solr {core} {cacheName} Size graph_args -l 0 -graph_category solr +graph_category search graph_vlabel Size size.label Size size.draw LINE2 @@ -269,7 +269,7 @@ QPSMAIN_GRAPH_TPL = """graph_title Solr {core} {handler} Request per second graph_args --base 1000 -r --lower-limit 0 graph_scale no graph_vlabel request / second -graph_category solr +graph_category search graph_period second graph_order {gorder} {cores_qps_graphs}""" @@ -284,7 +284,7 @@ REQUESTTIMES_GRAPH_TPL = """multigraph {core}_requesttimes graph_title Solr {core} {handler} Time per request graph_args -l 0 graph_vlabel millis -graph_category solr +graph_category search savgtimeperrequest_{core}.label {core} Avg time per request savgtimeperrequest_{core}.type GAUGE savgtimeperrequest_{core}.graph yes @@ -299,12 +299,12 @@ s99thpcrequesttime_{core}.graph yes NUMDOCS_GRAPH_TPL = """graph_title Solr Docs %s graph_vlabel docs docs.label Docs -graph_category solr""" +graph_category search""" INDEXSIZE_GRAPH_TPL = """graph_args --base 1024 -l 0 graph_vlabel Bytes graph_title Index Size -graph_category solr +graph_category search graph_info Solr Index Size. graph_order {cores} {cores_config} @@ -318,7 +318,7 @@ INDEXSIZECORE_GRAPH_TPL = """{core}.label {core} MEMORYUSAGE_GRAPH_TPL = """graph_args --base 1024 -l 0 --upper-limit {availableram} graph_vlabel Bytes graph_title Solr memory usage -graph_category solr +graph_category search graph_info Solr Memory Usage. used.label Used max.label Max diff --git a/plugins/solr/solrmulticore b/plugins/solr/solrmulticore index 8eab6ece..b210246f 100755 --- a/plugins/solr/solrmulticore +++ b/plugins/solr/solrmulticore @@ -17,7 +17,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # -# This plugin monitors a SOLR server configured for multicore by automaticaly +# This plugin monitors a SOLR server configured for multicore by automatically # getting core names from SOLR default page. # # Tested on SOLR 1.4.0 @@ -158,7 +158,7 @@ if len(sys.argv) > 1: print 'graph_title %s' % ( PLUGINOPTIONSLIST[params['valueName']]['label'] ) print "graph_args --base 1000"; print 'graph_vlabel Size %s' % params['valueName'] - print 'graph_category Solr' + print 'graph_category search' print 'graph_info Info for cores: %s' % ( ",".join(params['cores']) ) # Iterations for core datas diff --git a/plugins/solr/wfsolr_ b/plugins/solr/wfsolr_ index 5f71706a..fa5d5025 100755 --- a/plugins/solr/wfsolr_ +++ b/plugins/solr/wfsolr_ @@ -170,7 +170,7 @@ try { echo "graph_args --base 1024 -l 0\n"; } - echo "graph_category Solr $core\n"; + echo "graph_category search $core\n"; echo "graph_title $item $property\n"; echo "graph_vlabel $property\n"; if ($core !== null) diff --git a/plugins/sourceds/srcds_cpu b/plugins/sourceds/srcds_cpu index 56c32f8b..9a1b5099 100755 --- a/plugins/sourceds/srcds_cpu +++ b/plugins/sourceds/srcds_cpu @@ -29,7 +29,7 @@ if (-l $0) { } push(@INC, dirname($0)); -# Load Rcon module or exit with failuer message +# Load Rcon module or exit with failure message if (!eval "require Rcon") { print "Failed to load Rcon module. "; print "Make sure Rcon.pm is copied to Munin plugins directory.\n"; @@ -114,7 +114,7 @@ sub print_config { print("graph_title Server CPU usage at $HOST:$PORT\n", "graph_args --base 1000\n", "graph_vlabel CPU\n", - "graph_category SourceDS\n", + "graph_category games\n", "graph_info The CPU usage of Source game server, such as HL2, CS:S and DoD:S.\n"); print ("cpu.label CPU\n", diff --git a/plugins/sourceds/srcds_fps b/plugins/sourceds/srcds_fps index 99db64f9..c2625b7e 100755 --- a/plugins/sourceds/srcds_fps +++ b/plugins/sourceds/srcds_fps @@ -30,7 +30,7 @@ if (-l $0) { } push(@INC, dirname($0)); -# Load Rcon module or exit with failuer message +# Load Rcon module or exit with failure message if (!eval "require Rcon") { print "Failed to load Rcon module. "; print "Make sure Rcon.pm is copied to Munin plugins directory.\n"; @@ -116,7 +116,7 @@ sub print_config { print("graph_title Server FPS at $HOST:$PORT\n", "graph_args --base 1000\n", "graph_vlabel FPS\n", - "graph_category SourceDS\n", + "graph_category games\n", "graph_info The number of frames per second generated by Source game server, such as HL2, CS:S and DoD:S.\n"); print ("fps.label FPS\n", diff --git a/plugins/sourceds/srcds_inout b/plugins/sourceds/srcds_inout index 8747574f..2ae34792 100755 --- a/plugins/sourceds/srcds_inout +++ b/plugins/sourceds/srcds_inout @@ -29,7 +29,7 @@ if (-l $0) { } push(@INC, dirname($0)); -# Load Rcon module or exit with failuer message +# Load Rcon module or exit with failure message if (!eval "require Rcon") { print "Failed to load Rcon module. "; print "Make sure Rcon.pm is copied to Munin plugins directory.\n"; @@ -121,7 +121,7 @@ sub print_config { print("graph_title Server traffic at $HOST:$PORT\n", "graph_args --base 1000\n", "graph_vlabel bits in (-) / out (+) per second\n", - "graph_category SourceDS\n", + "graph_category games\n", "graph_info The traffic of incoming and outgoing data from Source game server, such as HL2, CS:S and DoD:S.\n"); print ( diff --git a/plugins/sourceds/srcds_players b/plugins/sourceds/srcds_players index 4c942587..ce2c1621 100755 --- a/plugins/sourceds/srcds_players +++ b/plugins/sourceds/srcds_players @@ -30,7 +30,7 @@ if (-l $0) { } push(@INC, dirname($0)); -# Load Rcon module or exit with failuer message +# Load Rcon module or exit with failure message if (!eval "require Rcon") { print "Failed to load Rcon module. "; print "Make sure Rcon.pm is copied to Munin plugins directory.\n"; @@ -92,7 +92,7 @@ sub print_config { print("graph_title Number of players at $HOST:$PORT\n", "graph_args --base 1000\n", "graph_vlabel Players\n", - "graph_category SourceDS\n", + "graph_category games\n", "graph_info The number of players on Source game server, such as HL2, CS:S and DoD:S.\n"); print ("players.label Players\n", diff --git a/plugins/sourceds/srcds_uptime b/plugins/sourceds/srcds_uptime index cb7cdb35..0e4011e2 100755 --- a/plugins/sourceds/srcds_uptime +++ b/plugins/sourceds/srcds_uptime @@ -28,7 +28,7 @@ if (-l $0) { } push(@INC, dirname($0)); -# Load Rcon module or exit with failuer message +# Load Rcon module or exit with failure message if (!eval "require Rcon") { print "Failed to load Rcon module. "; print "Make sure Rcon.pm is copied to Munin plugins directory.\n"; @@ -91,7 +91,7 @@ sub print_config { print("graph_title Game server uptime at $HOST:$PORT\n", "graph_args --base 1000\n", "graph_vlabel Uptime (hours)\n", - "graph_category SourceDS\n", + "graph_category games\n", "graph_info The uptime for Source game server, such as HL2, CS:S and DoD:S.\n"); print ("uptime.label Uptime\n", diff --git a/plugins/mail/sa-learn b/plugins/spamassasin/sa-learn similarity index 100% rename from plugins/mail/sa-learn rename to plugins/spamassasin/sa-learn diff --git a/plugins/sphinx/sphindex_ b/plugins/sphinx/sphindex_ index 5cb775dd..7c37f2c4 100755 --- a/plugins/sphinx/sphindex_ +++ b/plugins/sphinx/sphindex_ @@ -18,49 +18,56 @@ # This plugin requires pythons sphinxsearch module which can be installed via easy_install. # # ## Installation -# Copy file to directory /usr/share/munin/pligins/ and create symbolic links for each index you wish to monitor. +# Copy file to directory /usr/share/munin/pligins/ and create symbolic links for each index you +# wish to monitor. # For example, if you've got indexes called index1 and index2 create these symlinks: # # ln -s /usr/share/munin/plugins/sphindex_ /etc/munin/plugins/sphindex_index1 # ln -s /usr/share/munin/plugins/sphindex_ /etc/munin/plugins/sphindex_index2 # -# If you run munin-node at different box than Sphinxsearch you can specify hostname and port options in munin-node.conf: +# If you run munin-node at different box than Sphinxsearch you can specify hostname and port +# options in munin-node.conf: # # [sphindex_*] # env.server 10.216.0.141 # env.port 9312 # -#%# capabilities=autoconf -#%# family=contrib +# #%# capabilities=autoconf +# #%# family=contrib -import os, sys, sphinxsearch -progName = sys.argv[0] -indexName = progName[progName.find("_")+1:] +import os +import sys + +import sphinxsearch + + +prog_name = sys.argv[0] +index_name = prog_name[prog_name.find("_")+1:] if len(sys.argv) == 2 and sys.argv[1] == "autoconf": - print "yes" + print("yes") elif len(sys.argv) == 2 and sys.argv[1] == "config": warning = "0:" critical = "0:" - if "warning" in os.environ and os.environ["warning"] != None: + if "warning" in os.environ and os.environ["warning"]: warning = os.environ["warning"] - if "critical" in os.environ and os.environ["critical"] != None: + if "critical" in os.environ and os.environ["critical"]: critical = os.environ["critical"] - print "graph_title Sphinx index %s stats" % indexName - print "graph_vlabel docs count" - print "graph_category search" - print "documents_count.warning %s" % warning - print "documents_count.critical %s" % critical - print "documents_count.label Documents count in index" - print "graph_args --base 1000 -l 0" + print("graph_title Sphinx index %s stats" % index_name) + print("graph_vlabel docs count") + print("graph_category search") + print("documents_count.warning %s" % warning) + print("documents_count.critical %s" % critical) + print("documents_count.label Documents count in index") + print("graph_args --base 1000 -l 0") else: - if "server" in os.environ and os.environ["server"] != None: + if "server" in os.environ and os.environ["server"]: server = os.environ["server"] else: - server = "localhost" + server = "localhost" - if "port" in os.environ and os.environ["port"] != None: + if "port" in os.environ and os.environ["port"]: try: port = int(os.environ["port"]) except ValueError: @@ -71,7 +78,7 @@ else: client = sphinxsearch.SphinxClient() client.SetServer(server, port) client.SetLimits(0, 1, 0, 0) - result = client.Query("", indexName) - docCount = result["total_found"] + result = client.Query("", index_name) + doc_count = result["total_found"] - print "documents_count.value %d" % docCount + print("documents_count.value %d" % doc_count) diff --git a/plugins/sphinx/sphinx_connections b/plugins/sphinx/sphinx_connections index 029a6db2..fbb84e54 100755 --- a/plugins/sphinx/sphinx_connections +++ b/plugins/sphinx/sphinx_connections @@ -23,10 +23,10 @@ if (isset($argc) && $argc > 1) { if ($argv[1] == 'config') { echo "graph_title Sphinx Connections in last 5 minutes\n" . "graph_info This graph shows the number of connections for last 5 minutes\n" . - "graph_category sphinx\n" . + "graph_category search\n" . "graph_args --base 1000 --lower-limit 0\n" . "graph_vlabel Connections\n" . - "graph_info The number of current connections with respect to the max_connections setting.graph_category sphinx\n" . + "graph_info The number of current connections with respect to the max_connections setting.graph_category search\n" . "graph_order current\n" . "graph_total Total\n" . "current.label In Use\n" . diff --git a/plugins/sphinx/sphinx_documents b/plugins/sphinx/sphinx_documents index 0e291c9d..9b73e15d 100755 --- a/plugins/sphinx/sphinx_documents +++ b/plugins/sphinx/sphinx_documents @@ -17,7 +17,7 @@ case $1 in graph_title Documents per index graph_vlabel number graph_scale no -graph_category sphinx +graph_category search graph_info Report number of documents (using indextool) by Sphinx Index. EOM for i in `ls $idxpath/*.sph`; do diff --git a/plugins/sphinx/sphinx_queries b/plugins/sphinx/sphinx_queries index 7a9d9527..57db16e2 100755 --- a/plugins/sphinx/sphinx_queries +++ b/plugins/sphinx/sphinx_queries @@ -23,7 +23,7 @@ if (isset($argc) && $argc > 1) { if ($argv[1] == 'config') { echo "graph_title Sphinx Queries in last 5 minutes\n" . "graph_info This graph shows the number of queries for last 5 minutes\n" . - "graph_category sphinx\n" . + "graph_category search\n" . "graph_args --base 1000 --lower-limit 0\n" . "graph_vlabel Connections\n" . "graph_info The number of current queries\n" . diff --git a/plugins/spotweb/spotweb_average b/plugins/spotweb/spotweb_average index c8f293a9..2f31ee23 100755 --- a/plugins/spotweb/spotweb_average +++ b/plugins/spotweb/spotweb_average @@ -30,7 +30,7 @@ # env.database spotweb # Spotweb database (optional) # # [spotweb_average] -# env.period 1800 # The period over wich to check in seconds, default to 30 minutes (1800 seconds) +# env.period 1800 # The period over which to check in seconds, default to 30 minutes (1800 seconds) # env.watchlist yes # Show items in the watchlist (period independent) # # @@ -51,7 +51,7 @@ if(defined $ARGV[0] && $ARGV[0] eq 'config') print <new("UTF-8", "LATIN1"); # $converted = $converter->convert("Text to convert"); # First all the simple readings -foreach my $attr qw (albums artists genres songs) { +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+)$/) { @@ -148,7 +148,7 @@ foreach my $attr qw (albums artists genres songs) { if ($config) { print "graph_title Number of ${attr}\n"; print "graph_scale no\n"; - print "graph_category Squeezebox\n"; + print "graph_category radio\n"; print "${attr}.label ${attr}\n"; } else { print "${attr}.value $number\n"; @@ -167,7 +167,7 @@ if (uri_unescape($conn->getline) =~ /^years\s+count:(\d+)/) { if ($config) { # config run print "graph_title Albums per year\n"; - print "graph_category Squeezebox\n"; + print "graph_category radio\n"; print "graph_args --base 1000 -l 0\n"; foreach my $year (@years) { if ($year =~ /year\:(\d+)/) { @@ -199,7 +199,7 @@ foreach my $attr ("signalstrength", "mixer volume") { print "multigraph squeezebox_${attr_print}\n"; if ($config) { print "graph_title " . ucfirst ($attr) . "\n"; - print "graph_category Squeezebox\n"; + print "graph_category radio\n"; } $conn->print ("player count ?"); (my $no_of_players = uri_unescape ($conn->getline)) =~ s/player count\s+//; diff --git a/plugins/squid/squid b/plugins/squid/squid index 3a84ef86..885b9438 100755 --- a/plugins/squid/squid +++ b/plugins/squid/squid @@ -125,7 +125,7 @@ if($ARGV[0] and $ARGV[0] eq "config") $config{$name}{'graph'}{'vlabel'} = '%'; $config{$name}{'graph'}{'args'} = '--lower-limit 0 --upper-limit 100'; $config{$name}{'graph'}{'scale'} = 'no'; - $config{$name}{'graph'}{'category'} = 'squid'; + $config{$name}{'graph'}{'category'} = 'webserver'; $config{$name}{'graph'}{'order'} = 'all bytes memory disk'; $config{$name}{'field'}{'all'}{'draw'} = 'AREA'; $config{$name}{'field'}{'all'}{'label'} = 'Hits of all requests'; @@ -145,7 +145,7 @@ if($ARGV[0] and $ARGV[0] eq "config") $config{'squid_traffic'}{'graph'}{'title'} = 'Traffic statistics'; $config{'squid_traffic'}{'graph'}{'vlabel'} = 'bytes in (-) / out (+) per second'; $config{'squid_traffic'}{'graph'}{'args'} = '--base 1000'; - $config{'squid_traffic'}{'graph'}{'category'} = 'squid'; + $config{'squid_traffic'}{'graph'}{'category'} = 'webserver'; $config{'squid_traffic'}{'graph'}{'order'} = 'client_http_in server_all_in server_http_in server_ftp_in ' . 'server_other_in icp_in icp_q_in icp_r_in cd_in client_http_hit_out ' . 'client_http_out server_all_out server_http_out server_ftp_out ' . @@ -179,7 +179,7 @@ if($ARGV[0] and $ARGV[0] eq "config") $config{'squid_ipcache'}{'graph'}{'title'} = 'IP cache statistics'; $config{'squid_ipcache'}{'graph'}{'vlabel'} = 'Count'; $config{'squid_ipcache'}{'graph'}{'args'} = '--base 1000';# --logarithmic'; - $config{'squid_ipcache'}{'graph'}{'category'} = 'squid'; + $config{'squid_ipcache'}{'graph'}{'category'} = 'webserver'; $config{'squid_ipcache'}{'graph'}{'order'} = 'request hits misses numhits neghits invdreqests entries'; $config{'squid_ipcache'}{'field'}{'entries'}{'label'} = 'Entries'; $config{'squid_ipcache'}{'field'}{'request'}{'label'} = 'Requests'; @@ -195,7 +195,7 @@ if($ARGV[0] and $ARGV[0] eq "config") $config{'squid_requests'}{'graph'}{'title'} = 'Requests statistics'; $config{'squid_requests'}{'graph'}{'vlabel'} = 'errors (-) / requests (+) per second'; $config{'squid_requests'}{'graph'}{'args'} = '--base 1000'; - $config{'squid_requests'}{'graph'}{'category'} = 'squid'; + $config{'squid_requests'}{'graph'}{'category'} = 'webserver'; $config{'squid_requests'}{'graph'}{'order'} = 'client_http_errors server_all_errors server_http_errors ' . 'server_ftp_errors server_other_errors ' . 'client_http_hits aborted_requests client_http_requests ' . @@ -226,7 +226,7 @@ if($ARGV[0] and $ARGV[0] eq "config") $config{'squid_storedir'}{'graph'}{'title'} = 'Store directory statistics'; $config{'squid_storedir'}{'graph'}{'vlabel'} = 'bytes'; $config{'squid_storedir'}{'graph'}{'args'} = '--base 1000'; - $config{'squid_storedir'}{'graph'}{'category'} = 'squid'; + $config{'squid_storedir'}{'graph'}{'category'} = 'webserver'; $config{'squid_storedir'}{'graph'}{'order'} = 'maximum current'; $config{'squid_storedir'}{'field'}{'maximum'}{'label'} = 'Maximum'; $config{'squid_storedir'}{'field'}{'maximum'}{'draw'} = 'AREA'; @@ -238,7 +238,7 @@ if($ARGV[0] and $ARGV[0] eq "config") $config{'squid_memory'}{'graph'}{'title'} = 'Store memory statistics'; $config{'squid_memory'}{'graph'}{'vlabel'} = 'bytes'; $config{'squid_memory'}{'graph'}{'args'} = '--base 1000'; - $config{'squid_memory'}{'graph'}{'category'} = 'squid'; + $config{'squid_memory'}{'graph'}{'category'} = 'webserver'; $config{'squid_memory'}{'graph'}{'order'} = 'mi_tsia mi_ob mi_sb mi_hb mi_fsb mi_fob mi_tiu mi_tf mi_ts ta ma mu'; $config{'squid_memory'}{'field'}{'mi_tsia'}{'label'} = '[mallinfo()] Total space in arena'; $config{'squid_memory'}{'field'}{'mi_ob'}{'label'} = '[mallinfo()] Ordinary blocks'; @@ -261,7 +261,7 @@ if($ARGV[0] and $ARGV[0] eq "config") $config{'squid_meanobject'}{'graph'}{'title'} = 'Mean object size'; $config{'squid_meanobject'}{'graph'}{'vlabel'} = 'bytes'; $config{'squid_meanobject'}{'graph'}{'args'} = '--base 1000'; - $config{'squid_meanobject'}{'graph'}{'category'} = 'squid'; + $config{'squid_meanobject'}{'graph'}{'category'} = 'webserver'; $config{'squid_meanobject'}{'graph'}{'order'} = 'mos'; $config{'squid_meanobject'}{'field'}{'mos'}{'label'} = 'Mean object size'; $config{'squid_meanobject'}{'field'}{'mos'}{'draw'} = 'LINE1'; @@ -270,7 +270,7 @@ if($ARGV[0] and $ARGV[0] eq "config") $config{'squid_cpu'}{'graph'}{'title'} = 'CPU usage'; $config{'squid_cpu'}{'graph'}{'vlabel'} = '%'; $config{'squid_cpu'}{'graph'}{'args'} = '--base 1000 --lower-limit 0'; - $config{'squid_cpu'}{'graph'}{'category'} = 'squid'; + $config{'squid_cpu'}{'graph'}{'category'} = 'webserver'; $config{'squid_cpu'}{'graph'}{'scale'} = 'no'; $config{'squid_cpu'}{'graph'}{'order'} = 'cur av5 av60'; $config{'squid_cpu'}{'field'}{'cur'}{'label'} = 'Current'; @@ -288,7 +288,7 @@ if($ARGV[0] and $ARGV[0] eq "config") $config{'squid_ids'}{'graph'}{'title'} = 'Internal data structures'; $config{'squid_ids'}{'graph'}{'vlabel'} = 'Count'; $config{'squid_ids'}{'graph'}{'args'} = '--lower-limit 0'; - $config{'squid_ids'}{'graph'}{'category'} = 'squid'; + $config{'squid_ids'}{'graph'}{'category'} = 'webserver'; $config{'squid_ids'}{'graph'}{'order'} = 'se sewmo hoci odo'; $config{'squid_ids'}{'field'}{'se'}{'label'} = 'StoreEntries'; $config{'squid_ids'}{'field'}{'se'}{'draw'} = 'LINE1'; @@ -305,7 +305,7 @@ if($ARGV[0] and $ARGV[0] eq "config") $config{$name}{'graph'}{'title'} = "Median service times in ${time} minuts"; $config{$name}{'graph'}{'vlabel'} = 'seconds'; $config{$name}{'graph'}{'args'} = '--base 1000'; - $config{$name}{'graph'}{'category'} = 'squid'; + $config{$name}{'graph'}{'category'} = 'webserver'; $config{$name}{'graph'}{'order'} = 'hra cm ch nh nmr dl iq'; $config{$name}{'field'}{'hra'}{'label'} = 'HTTP Requests (All)'; $config{$name}{'field'}{'hra'}{'draw'} = 'LINE1'; @@ -460,3 +460,6 @@ foreach my $mgraph (sort keys(%data)) print "\n"; } +# for Munin Plugin Gallery +# graph_category webserver + diff --git a/plugins/squid/squid_efficiency b/plugins/squid/squid_efficiency index 86d0a24a..0e4a9ac8 100755 --- a/plugins/squid/squid_efficiency +++ b/plugins/squid/squid_efficiency @@ -60,7 +60,7 @@ port=${squidport:-3128} test "$1" = "config" && { echo 'graph_title Squid Efficiency' echo 'graph_info This graph shows the proxy efficiency over the last five mins.' - echo 'graph_category squid' + echo 'graph_category webserver' echo "graph_args --lower-limit 0 --upper-limit 100" echo 'graph_vlabel %' echo 'request.label request hits' diff --git a/plugins/squid/squid_times b/plugins/squid/squid_times index 5f3ef5e7..23f935e0 100755 --- a/plugins/squid/squid_times +++ b/plugins/squid/squid_times @@ -30,7 +30,7 @@ fi if [ "$1" = "config" ]; then echo 'graph_title Squid Median Services Times' echo 'graph_info This graph shows the proxy median services response times.' - echo 'graph_category squid' + echo 'graph_category webserver' echo 'graph_args --lower-limit 0' echo 'graph_vlabel median response times (s)' diff --git a/plugins/services/hostdenied b/plugins/ssh/hostdenied similarity index 96% rename from plugins/services/hostdenied rename to plugins/ssh/hostdenied index a798b17e..9996f2d6 100755 --- a/plugins/services/hostdenied +++ b/plugins/ssh/hostdenied @@ -44,9 +44,8 @@ # ------------------------------------------------------------------------------------------------------ DENY="/etc/hosts.deny" -STATEDIR="/var/lib/munin/plugin-state" # directory where plugin can keep their working files NAME="$(basename $0)" # component of naming temporary files -STATEFILE="$STATEDIR/$NAME.state" +STATEFILE="$MUNIN_PLUGSTATE/$NAME.state" COLOUR=(FF0000 DA0024 B60048 91006D 6D0091 4800B6 2400DA 0000FF) # hot to cold colours # ------------------------------------------------------------------------------------------------------ @@ -62,7 +61,7 @@ cat << EOF graph_title denied sshd access in $DENY graph_args --base 1000 -l 0 graph_vlabel Hosts denied -graph_category services +graph_category security age0.label added last 24h age0.draw AREA age0.colour ${COLOUR[0]} diff --git a/plugins/network/ssh/openssh-denyhosts b/plugins/ssh/openssh-denyhosts similarity index 94% rename from plugins/network/ssh/openssh-denyhosts rename to plugins/ssh/openssh-denyhosts index 163ac1a8..46c13bcd 100755 --- a/plugins/network/ssh/openssh-denyhosts +++ b/plugins/ssh/openssh-denyhosts @@ -15,7 +15,7 @@ mktemp -t } AUTH_LOG=${logfile:-/var/log/auth.log} -STATEFILE=/var/lib/munin/plugin-state/sshd.offset +STATEFILE=$MUNIN_PLUGSTATE/sshd.offset LOGTAIL=${logtail:-`which logtail`} if [ "$1" = "autoconf" ]; then @@ -31,7 +31,7 @@ fi if [ "$1" = "config" ]; then echo 'graph_title SSH Statistics' echo 'graph_order refused invalid accepted' - echo 'graph_category ssh' + echo 'graph_category security' echo 'graph_vlabel Count' echo 'graph_scale no' diff --git a/plugins/network/ssh/sshd_invalid_countries b/plugins/ssh/sshd_invalid_countries similarity index 98% rename from plugins/network/ssh/sshd_invalid_countries rename to plugins/ssh/sshd_invalid_countries index 04f8f929..e36b3201 100755 --- a/plugins/network/ssh/sshd_invalid_countries +++ b/plugins/ssh/sshd_invalid_countries @@ -52,7 +52,7 @@ if (isset($argv[1]) && $argv[1] == 'config') { echo 'graph_title SSHD invalid countries from ' . SYSLOG . "\n"; echo 'graph_args --base 1000 -l 0' . "\n"; echo 'graph_vlabel number of invalid access per country' . "\n"; - echo 'graph_category system' . "\n"; + echo 'graph_category security' . "\n"; echo 'graph_info This graph shows the countries of invalid access to sshd.' . "\n"; foreach (get_sshd_invalid_countries() as $country => $cnt) { echo $country . '.label ' . $country . "\n"; diff --git a/plugins/network/ssh/sshd_invalid_countries_ruby b/plugins/ssh/sshd_invalid_countries_ruby similarity index 98% rename from plugins/network/ssh/sshd_invalid_countries_ruby rename to plugins/ssh/sshd_invalid_countries_ruby index ba79454a..9a6c11f4 100755 --- a/plugins/network/ssh/sshd_invalid_countries_ruby +++ b/plugins/ssh/sshd_invalid_countries_ruby @@ -71,7 +71,7 @@ when 'config' puts 'graph_title SSHD invalid countries from ' + SYSLOG puts 'graph_args --base 1000 -l 0' puts 'graph_vlabel number of invalid access per country' - puts 'graph_category system' + puts 'graph_category security' puts 'graph_info This graph shows the countries of invalid access to sshd.' getInvalids.each {|k,v| puts k + '.label ' + k} exit 0 diff --git a/plugins/other/sshd_log b/plugins/ssh/sshd_log similarity index 97% rename from plugins/other/sshd_log rename to plugins/ssh/sshd_log index ea84ae2c..608f49ec 100755 --- a/plugins/other/sshd_log +++ b/plugins/ssh/sshd_log @@ -30,7 +30,6 @@ # LOG=${logfile:-/var/log/secure} -CATEGORY=${category:-system} if [ "$1" = "autoconf" ]; then @@ -48,7 +47,7 @@ if [ "$1" = "config" ]; then echo 'graph_title SSHD login stats from' $LOG echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel logins' - echo 'graph_category' $CATEGORY + echo 'graph_category' security echo 'LogPass.label Successful password logins' echo 'LogPassPAM.label Successful login via PAM' diff --git a/plugins/ssl/ssl-certificate-expiry b/plugins/ssl/ssl-certificate-expiry new file mode 100755 index 00000000..7c69face --- /dev/null +++ b/plugins/ssl/ssl-certificate-expiry @@ -0,0 +1,127 @@ +#!/bin/sh +# -*- sh -*- + +: << =cut + +=head1 NAME + +ssl-certificate-expiry - Plugin to monitor CERTificate expiration on multiple services and ports + +=head1 CONFIGURATION + + [ssl-certificate-expiry] + env.services www.service.tld blah.example.net_PORT + +To set warning and critical levels do like this: + + [ssl-certificate-expiry] + env.services ... + env.warning 30: + +Alternatively, if you want to monitor hosts separately, you can create multiple symlinks named as follows. + + ssl-certificate-expiry_HOST_PORT + +For example: + + ssl-certificate-expiry_www.example.net + ssl-certificate-expiry_www.example.org_443 + ssl-certificate-expiry_192.0.2.42_636 + ssl-certificate-expiry_2001:0DB8::badc:0fee_485 + +=head1 AUTHOR + +Pactrick Domack (ssl_) +Olivier Mehani (ssl-certificate-expiry) + +Copyright (C) 2013 Patrick Domack +Copyright (C) 2017 Olivier Mehani + +=head1 LICENSE + +=cut + +# shellcheck disable=SC1090 +. "${MUNIN_LIBDIR}/plugins/plugin.sh" + +if [ "${MUNIN_DEBUG}" = 1 ]; then + set -x +fi + +HOSTPORT=${0##*ssl-certificate-expiry_} + +if [ "${HOSTPORT}" != "${0}" ] \ + && [ ! -z "${HOSTPORT}" ]; then + services="${HOSTPORT}" +fi + + +# Read data including a certificate from stdin and output the (fractional) number of days left +# until the expiry of this certificate. The output is empty if parsing failed. +parse_valid_days_from_certificate() { + local input_data + local valid_until_string + local valid_until_epoch + local now_epoch + local input_data + input_data=$(cat) + if echo "$input_data" | grep -q -- "-----BEGIN CERTIFICATE-----"; then + valid_until_string=$(echo "$input_data" | openssl x509 -noout -enddate \ + | grep "^notAfter=" | cut -f 2 -d "=") + if [ -n "$valid_until_string" ]; then + valid_until_epoch=$(date --date="$valid_until_string" +%s) + if [ -n "$valid_until_epoch" ]; then + now_epoch=$(date +%s) + # calculate the number of days left + echo "$valid_until_epoch" "$now_epoch" | awk '{ print(($1 - $2) / (24 * 3600)); }' + fi + fi + fi +} + + +print_expire_days() { + local host="$1" + local port="$2" + + # Wrap IPv6 addresses in square brackets + echo "$host" | grep -q ':' && host="[$host]" + + echo "" | openssl s_client -CApath /etc/ssl/certs \ + -servername "$host" -connect "${host}:${port}" 2>/dev/null \ + | parse_valid_days_from_certificate +} + + +case $1 in + config) + + echo "graph_title SSL Certificates Expiration" + echo 'graph_args --base 1000' + echo 'graph_vlabel days left' + echo 'graph_category security' + echo "graph_info This graph shows the days left for the certificate" + for service in $services; do + fieldname=$(clean_fieldname "$service") + echo "${fieldname}.label $(echo "${service}" | sed 's/_/:/')" + print_thresholds "${fieldname}" + done + + exit 0 + ;; +esac + + +for service in $services; do + if echo "$service" | grep -q "_"; then + host=$(echo "$service" | cut -f 1 -d "_") + port=$(echo "$service" | cut -f 2 -d "_") + else + host=$service + port=443 + fi + fieldname="$(clean_fieldname "$service")" + valid_days=$(print_expire_days "$host" "$port") + [ -z "$valid_days" ] && valid_days="U" + printf "%s.value %s\n" "$fieldname" "$valid_days" +done diff --git a/plugins/ssl/ssl_ b/plugins/ssl/ssl_ old mode 100644 new mode 100755 index 7f02b99d..496453f1 --- a/plugins/ssl/ssl_ +++ b/plugins/ssl/ssl_ @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh # -*- sh -*- : << =cut @@ -26,9 +26,42 @@ Copyright (C) 2013 Patrick Domack =cut -. $MUNIN_LIBDIR/plugins/plugin.sh +# shellcheck disable=SC1090 +. "$MUNIN_LIBDIR/plugins/plugin.sh" + +ARGS=${0##*ssl_} +if echo "$ARGS" | grep -q "_"; then + SITE=$(echo "$ARGS" | cut -f 1 -d "_") + PORT=$(echo "$ARGS" | cut -f 2 -d "_") +else + SITE=$ARGS + PORT=443 +fi + + +# Read data including a certificate from stdin and output the (fractional) number of days left +# until the expiry of this certificate. The output is empty if parsing failed. +parse_valid_days_from_certificate() { + local input_data + local valid_until_string + local valid_until_epoch + local now_epoch + local input_data + input_data=$(cat) + if echo "$input_data" | grep -q -- "-----BEGIN CERTIFICATE-----"; then + valid_until_string=$(echo "$input_data" | openssl x509 -noout -enddate \ + | grep "^notAfter=" | cut -f 2 -d "=") + if [ -n "$valid_until_string" ]; then + valid_until_epoch=$(date --date="$valid_until_string" +%s) + if [ -n "$valid_until_epoch" ]; then + now_epoch=$(date +%s) + # calculate the number of days left + echo "$valid_until_epoch" "$now_epoch" | awk '{ print(($1 - $2) / (24 * 3600)); }' + fi + fi + fi +} -SITE=${0##*ssl_} case $1 in config) @@ -36,7 +69,7 @@ case $1 in echo "graph_title $SITE SSL Certificate Expire" echo 'graph_args --base 1000' echo 'graph_vlabel days left' - echo 'graph_category ssl' + echo 'graph_category security' echo "graph_info This graph shows the days left for the certificate being served by $SITE" echo 'expire.label days' print_warning expire @@ -46,8 +79,9 @@ case $1 in ;; esac -cert=$(echo "" | openssl s_client -CApath /etc/ssl/certs -servername "${SITE}" -connect "${SITE}:443" 2>/dev/null); +cert=$(echo "" | openssl s_client -CApath /etc/ssl/certs -servername "${SITE}" -connect "${SITE}:${PORT}" 2>/dev/null); -if [[ "${cert}" = *"-----BEGIN CERTIFICATE-----"* ]]; then - echo "${cert}" | openssl x509 -noout -enddate | awk -F= 'BEGIN { split("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec", month, " "); for (i=1; i<=12; i++) mdigit[month[i]] = i; } /notAfter/ { split($0,a,"="); split(a[2],b," "); split(b[3],time,":"); datetime=b[4] " " mdigit[b[1]] " " b[2] " " time[1] " " time[2] " " time[3]; days=(mktime(datetime)-systime())/86400; print "expire.value " days; }' -fi +days_left=$(echo "$cert" | parse_valid_days_from_certificate) +[ -n "$days_left" ] || days_left="U" + +printf "expire.value %s\n" "$days_left" diff --git a/plugins/streaming/packetship_ b/plugins/streaming/packetship_ index a9ec0964..b7465498 100755 --- a/plugins/streaming/packetship_ +++ b/plugins/streaming/packetship_ @@ -108,9 +108,7 @@ if [ "$1" = "config" ]; then # If dirty config capability is enabled then fall through # to output the data with the config information. - if [ "$MUNIN_CAP_DIRTYCONFIG" = "" ]; then - exit 0 - fi + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" != "1" ]; then exit 0; fi fi # If there are no pumps then output fake pump1 data and end. diff --git a/plugins/system/swapspace-info b/plugins/swap/swapspace-info similarity index 99% rename from plugins/system/swapspace-info rename to plugins/swap/swapspace-info index afa6e6cd..766afc36 100755 --- a/plugins/system/swapspace-info +++ b/plugins/swap/swapspace-info @@ -144,7 +144,7 @@ sub config print "graph_args --base 1024 -l 0 \n"; print "graph_vlabel Bytes\n"; print "graph_title Swapspace usage\n"; - print "graph_category system\n"; + print "graph_category memory\n"; print "graph_info This graph shows what the machine uses Swapspace for.\n"; print "graph_order "; diff --git a/plugins/swift/swift-async_ b/plugins/swift/swift-async_ index a39f7298..0e10c90d 100755 --- a/plugins/swift/swift-async_ +++ b/plugins/swift/swift-async_ @@ -35,7 +35,7 @@ except: try: if sys.argv[1] == 'config': print "graph_title Swift object async pending %s" % swift_server - print "graph_category swift" + print "graph_category fs" print "async_pending.type GAUGE" print "async_pending.label Async pending" print "async_pending.draw AREA" diff --git a/plugins/swift/swift-dispersion b/plugins/swift/swift-dispersion index 08d10249..73f0d3c5 100755 --- a/plugins/swift/swift-dispersion +++ b/plugins/swift/swift-dispersion @@ -27,46 +27,46 @@ import json try: if sys.argv[1] == "config": - print 'graph_title Swift cluster dispersion' - print 'graph_category swift' + print('graph_title Swift cluster dispersion') + print('graph_category fs') - print 'object_missing_two.type GAUGE' - print 'object_missing_two.label Objects missing two copies' - print 'object_retries.type GAUGE' - print 'object_retries.label Objects retries' - print 'object_copies_expected.type GAUGE' - print 'object_copies_expected.label Objects copies expected' - print 'object_missing_one.type GAUGE' - print 'object_missing_one.label Objects missing one copy' - print 'object_copies_found.type GAUGE' - print 'object_copies_found.label Objects copies found' - print 'object_missing_all.type GAUGE' - print 'object_missing_all.label Objects missing all copies' - print 'object_overlapping.type GAUGE' - print 'object_overlapping.label Objects overlapping partitions' + print('object_missing_two.type GAUGE') + print('object_missing_two.label Objects missing two copies') + print('object_retries.type GAUGE') + print('object_retries.label Objects retries') + print('object_copies_expected.type GAUGE') + print('object_copies_expected.label Objects copies expected') + print('object_missing_one.type GAUGE') + print('object_missing_one.label Objects missing one copy') + print('object_copies_found.type GAUGE') + print('object_copies_found.label Objects copies found') + print('object_missing_all.type GAUGE') + print('object_missing_all.label Objects missing all copies') + print('object_overlapping.type GAUGE') + print('object_overlapping.label Objects overlapping partitions') - print 'container_missing_two.type GAUGE' - print 'container_missing_two.label Containers missing two copies' - print 'container_retries.type GAUGE' - print 'container_retries.label Containers retries' - print 'container_copies_expected.type GAUGE' - print 'container_copies_expected.label Containers copies expected' - print 'container_missing_one.type GAUGE' - print 'container_missing_one.label Containers missing one copy' - print 'container_copies_found.type GAUGE' - print 'container_copies_found.label Containers copies found' - print 'container_missing_all.type GAUGE' - print 'container_missing_all.label Containers missing all copies' - print 'container_overlapping.type GAUGE' - print 'container_overlapping.label Containers overlapping paritions' + print('container_missing_two.type GAUGE') + print('container_missing_two.label Containers missing two copies') + print('container_retries.type GAUGE') + print('container_retries.label Containers retries') + print('container_copies_expected.type GAUGE') + print('container_copies_expected.label Containers copies expected') + print('container_missing_one.type GAUGE') + print('container_missing_one.label Containers missing one copy') + print('container_copies_found.type GAUGE') + print('container_copies_found.label Containers copies found') + print('container_missing_all.type GAUGE') + print('container_missing_all.label Containers missing all copies') + print('container_overlapping.type GAUGE') + print('container_overlapping.label Containers overlapping paritions') sys.exit(0) except IndexError: pass -with os.popen("swift-dispersion-report -j %s" \ - % os.getenv("SWIFT_DISPERSION_CONFIG", "/etc/swift/dispersion.conf")) as report: +with os.popen("swift-dispersion-report -j %s" + % os.getenv("SWIFT_DISPERSION_CONFIG", "/etc/swift/dispersion.conf")) as report: stats = json.load(report) for type_, values in stats.iteritems(): for key, value in values.iteritems(): - print "%s_%s.value %d" % (type_, key, value) + print("%s_%s.value %d" % (type_, key, value)) diff --git a/plugins/swift/swift-quarantined_ b/plugins/swift/swift-quarantined_ index 6757e040..5315d573 100755 --- a/plugins/swift/swift-quarantined_ +++ b/plugins/swift/swift-quarantined_ @@ -35,7 +35,7 @@ except: try: if sys.argv[1] == 'config': print "graph_title Swift objects quarantined %s" % swift_server - print "graph_category swift" + print "graph_category fs" print "quarantined_objects.type GAUGE" print "quarantined_objects.draw AREA" print "quarantined_objects.label Quarantined objects" diff --git a/plugins/swift/swift-replication-time_ b/plugins/swift/swift-replication-time_ index ce87e04d..f8380053 100755 --- a/plugins/swift/swift-replication-time_ +++ b/plugins/swift/swift-replication-time_ @@ -35,7 +35,7 @@ except: try: if sys.argv[1] == 'config': print "graph_title Swift object replication time %s" % swift_server - print "graph_category swift" + print "graph_category fs" print "graph_vlabel Time" print "object_replication_time.type GAUGE" print "object_replication_time.label Replication time" diff --git a/plugins/syncthing/example-graphs/strelaysrv_-1.png b/plugins/syncthing/example-graphs/strelaysrv_-1.png new file mode 100644 index 00000000..ea26f57d Binary files /dev/null and b/plugins/syncthing/example-graphs/strelaysrv_-1.png differ diff --git a/plugins/syncthing/example-graphs/strelaysrv_-2.png b/plugins/syncthing/example-graphs/strelaysrv_-2.png new file mode 100644 index 00000000..69f583eb Binary files /dev/null and b/plugins/syncthing/example-graphs/strelaysrv_-2.png differ diff --git a/plugins/syncthing/example-graphs/strelaysrv_-3.png b/plugins/syncthing/example-graphs/strelaysrv_-3.png new file mode 100644 index 00000000..9c82f011 Binary files /dev/null and b/plugins/syncthing/example-graphs/strelaysrv_-3.png differ diff --git a/plugins/syncthing/example-graphs/strelaysrv_-4.png b/plugins/syncthing/example-graphs/strelaysrv_-4.png new file mode 100644 index 00000000..244e3eda Binary files /dev/null and b/plugins/syncthing/example-graphs/strelaysrv_-4.png differ diff --git a/plugins/syncthing/example-graphs/syncthing_-1.png b/plugins/syncthing/example-graphs/syncthing_-1.png new file mode 100644 index 00000000..a71e34d2 Binary files /dev/null and b/plugins/syncthing/example-graphs/syncthing_-1.png differ diff --git a/plugins/syncthing/example-graphs/syncthing_-2.png b/plugins/syncthing/example-graphs/syncthing_-2.png new file mode 100644 index 00000000..a333f2a5 Binary files /dev/null and b/plugins/syncthing/example-graphs/syncthing_-2.png differ diff --git a/plugins/syncthing/example-graphs/syncthing_-3.png b/plugins/syncthing/example-graphs/syncthing_-3.png new file mode 100644 index 00000000..a1704be9 Binary files /dev/null and b/plugins/syncthing/example-graphs/syncthing_-3.png differ diff --git a/plugins/syncthing/example-graphs/syncthing_-4.png b/plugins/syncthing/example-graphs/syncthing_-4.png new file mode 100644 index 00000000..41dc6637 Binary files /dev/null and b/plugins/syncthing/example-graphs/syncthing_-4.png differ diff --git a/plugins/syncthing/example-graphs/syncthing_-5.png b/plugins/syncthing/example-graphs/syncthing_-5.png new file mode 100644 index 00000000..73a640dd Binary files /dev/null and b/plugins/syncthing/example-graphs/syncthing_-5.png differ diff --git a/plugins/syncthing/strelaysrv_ b/plugins/syncthing/strelaysrv_ new file mode 100755 index 00000000..5a30f27b --- /dev/null +++ b/plugins/syncthing/strelaysrv_ @@ -0,0 +1,160 @@ +#!/bin/sh +: <<=cut +=head1 NAME +strelaysrv_ - Plugin to monitor Syncthing relay server + +=head1 DESCRIPTION +This plugin gathers metrics from a Syncthing relay server. + +This plugin requires the jq utility : https://stedolan.github.io/jq/ +This plugin requires the curl utility : https://curl.haxx.se/ + +Available plugins : +strelaysrv_goroutine # +strelaysrv_num # +strelaysrv_proxied # +strelaysrv_transfer # +strelaysrv_uptime # + +=head1 CONFIGURATION +To make the plugin connect to the Syncthing relay server one has to use this type of +configuration +[strelaysrv_*] + +env.syncthing_relaysrv_host 127.0.0.1 +env.syncthing_relaysrv_port 22070 + +=head1 AUTHOR +Pierre-Alain TORET + +=head1 LICENSE +MIT +=cut + +syncthing_relaysrv_host=${syncthing_relaysrv_host:-} +syncthing_relaysrv_port=${syncthing_relaysrv_port:-} + +getstatus() { + "$CURL" -s "http://$syncthing_relaysrv_host:$syncthing_relaysrv_port/status" +} + +num() { + case $1 in + config) + cat <<'EOM' +graph_title Syncthing relay numbers +graph_category network +graph_vlabel numbers +strelaysrv_num_sessions.label sessions +strelaysrv_num_connections.label connections +strelaysrv_num_pending.label pending session keys +strelaysrv_num_proxies.label proxies +EOM + exit 0;; + *) + STATUS=$(getstatus) + NS=$(echo "$STATUS" | $JQ '.numActiveSessions ') + NC=$(echo "$STATUS" | $JQ '.numConnections ') + NK=$(echo "$STATUS" | $JQ '.numPendingSessionKeys ') + NP=$(echo "$STATUS" | $JQ '.numProxies ') + printf "strelaysrv_num_sessions.value %s\n" "$NS" + printf "strelaysrv_num_connections.value %s\n" "$NC" + printf "strelaysrv_num_pending.value %s\n" "$NK" + printf "strelaysrv_num_proxies.value %s\n" "$NP" + esac +} + +uptime() { + case $1 in + config) + cat <<'EOM' +graph_title Syncthing relay uptime +graph_vlabel uptime in seconds +graph_category network +strelaysrv_uptime.label uptime +EOM + exit 0;; + *) + STATUS=$(getstatus) + UPTIME=$(echo "$STATUS" | "$JQ" '.uptimeSeconds') + printf "strelaysrv_uptime.value %s\n" "$UPTIME" + esac +} + +goroutine() { + case $1 in + config) + cat <<'EOM' +graph_title Syncthing relay go routines +graph_vlabel number of go routines +graph_category network +strelaysrv_goroutine.label routines +EOM + exit 0;; + *) + STATUS=$(getstatus) + GOROUTINE=$(echo "$STATUS" | "$JQ" '.goNumRoutine') + printf "strelaysrv_goroutine.value %s\n" "$GOROUTINE" + esac +} + +proxied() { + case $1 in + config) + cat <<'EOM' +graph_title Syncthing relay total proxied bits +graph_category network +graph_vlabel bits +graph_args --base 1000 +strelaysrv_proxied.label bits +strelaysrv_proxied.cdef strelaysrv_proxied,8,* +EOM + exit 0;; + *) + STATUS=$(getstatus) + BP=$(echo "$STATUS" | "$JQ" '.bytesProxied ') + printf "strelaysrv_proxied.value %s\n" "$BP" + esac +} + +transfer() { + case $1 in + config) + cat <<'EOM' +graph_title Syncthing relay transfer rate +graph_category network +graph_vlabel bps +graph_args --base 1000 +strelaysrv_transfer.label bps +strelaysrv_transfer.cdef strelaysrv_transfer,1000,* +EOM + exit 0;; + *) + STATUS=$(getstatus) + TRANSFER=$(echo "$STATUS" | "$JQ" '.kbps10s1m5m15m30m60m[2] ') + printf "strelaysrv_transfer.value %s\n" "$TRANSFER" + esac +} + +cd "$(dirname "$0")" || exit + +CURL=$(which curl) +JQ=$(which jq) + +case $(basename "$0") in + strelaysrv_num) + num "$1" + exit 0;; + strelaysrv_uptime) + uptime "$1" + exit 0;; + strelaysrv_goroutine) + goroutine "$1" + exit 0;; + strelaysrv_proxied) + proxied "$1" + exit 0;; + strelaysrv_transfer) + transfer "$1" + exit 0;; +esac diff --git a/plugins/syncthing/syncthing_ b/plugins/syncthing/syncthing_ new file mode 100755 index 00000000..7bfd0cd0 --- /dev/null +++ b/plugins/syncthing/syncthing_ @@ -0,0 +1,171 @@ +#!/bin/sh +# -*- sh -*- +: <<=cut +=head1 NAME +syncthing_ - Plugin to monitor Syncthing server + +=head1 DESCRIPTION +This plugin gathers metrics from a Syncthing server. + +This plugin requires the jq utility : https://stedolan.github.io/jq/ +This plugin requires the cURL utility : https://curl.haxx.se/ + +Available plugins : +syncthing_cpu # +syncthing_mem # +syncthing_goroutine # +syncthing_transfer # +syncthing_uptime # + +=head1 CONFIGURATION +To make the plugin connect to the Syncthing server one has to use this type of +configuration +[syncthing_*] + +env.syncthing_apikey myapikey0123456789 +env.syncthing_host 127.0.0.1 +env.syncthing_port 8384 +env.syncthing_proto http + +=head1 AUTHOR +Pierre-Alain TORET + +=head1 LICENSE +MIT +=cut + +syncthing_apikey=${syncthing_apikey:-} +syncthing_proto=${syncthing_proto:-} +syncthing_host=${syncthing_host:-} +syncthing_port=${syncthing_port:-} + +getstatus() { + "$CURL" -s -X GET -H "X-API-Key: $syncthing_apikey" "$syncthing_proto://$syncthing_host:$syncthing_port/rest/system/status" +} + +cpu() { + case $1 in + config) + cat <<'EOM' +graph_title Syncthing server cpu usage +graph_args -u 100 +graph_vlabel % +graph_category network +syncthing_cpu.label cpu +EOM + exit 0;; + *) + STATUS=$(getstatus) + CPU=$(echo "$STATUS" | "$JQ" '.cpuPercent') + printf "syncthing_cpu.value %s\n" "$CPU" + esac +} + +mem() { + case $1 in + config) + cat <<'EOM' +graph_title Syncthing server memory +graph_category network +graph_order syncthing_mem_all syncthing_mem_sys +graph_args --base 1000 +graph_vlabel bits +syncthing_mem_all.label allocated +syncthing_mem_all.cdef syncthing_mem_all,8,* +syncthing_mem_sys.label obtained +syncthing_mem_sys.cdef syncthing_mem_sys,8,* +EOM + exit 0;; + *) + STATUS=$(getstatus) + ALL=$(echo "$STATUS" | "$JQ" '.alloc') + SYS=$(echo "$STATUS" | "$JQ" '.sys') + printf "syncthing_mem_all.value %s\n" "$ALL" + printf "syncthing_mem_sys.value %s\n" "$SYS" + esac +} + +uptime() { + case $1 in + config) + cat <<'EOM' +graph_title Syncthing server uptime +graph_vlabel uptime in seconds +graph_category network +syncthing_uptime.label uptime +EOM + exit 0;; + *) + STATUS=$(getstatus) + UPTIME=$(echo "$STATUS" | "$JQ" '.uptime') + printf "syncthing_uptime.value %s\n" "$UPTIME" + esac +} + +goroutine() { + case $1 in + config) + cat <<'EOM' +graph_title Syncthing server go routines +graph_vlabel number of go routines +graph_category network +syncthing_goroutine.label routines +EOM + exit 0;; + *) + STATUS=$(getstatus) + GOROUTINES=$(echo "$STATUS" | "$JQ" '.goroutines') + printf "syncthing_goroutine.value %s\n" "$GOROUTINES" + esac +} + +transfer() { + case $1 in + config) + cat <<'EOM' +graph_title Syncthing server total transfer +graph_category network +graph_order syncthing_transfer_down syncthing_transfer_up +graph_args --base 1000 +graph_vlabel bits in (-) / out (+) per ${graph_period} +syncthing_transfer_down.label received +syncthing_transfer_down.type COUNTER +syncthing_transfer_down.graph no +syncthing_transfer_down.cdef syncthing_transfer_down,8,* +syncthing_transfer_up.label bps +syncthing_transfer_up.type COUNTER +syncthing_transfer_up.negative syncthing_transfer_down +syncthing_transfer_up.cdef syncthing_transfer_up,8,* +EOM + exit 0;; + *) + CONNECTIONS=$("$CURL" -s -X GET -H "X-API-Key: $syncthing_apikey" "$syncthing_proto://$syncthing_host:$syncthing_port/rest/system/connections") + IBT=$(echo "$CONNECTIONS" | "$JQ" '.total | .inBytesTotal') + OBT=$(echo "$CONNECTIONS" | "$JQ" '.total | .outBytesTotal') + printf "syncthing_transfer_up.value %s\n" "$IBT" + printf "syncthing_transfer_down.value %s\n" "$OBT" + esac +} + +cd "$(dirname "$0")" || exit + +CURL=$(which curl) +JQ=$(which jq) + +case $(basename "$0") in + syncthing_cpu) + cpu "$1" + exit 0;; + syncthing_mem) + mem "$1" + exit 0;; + syncthing_uptime) + uptime "$1" + exit 0;; + syncthing_goroutine) + goroutine "$1" + exit 0;; + syncthing_transfer) + transfer "$1" + exit 0;; +esac diff --git a/plugins/snmp/snmp__synology b/plugins/synology/snmp__synology similarity index 99% rename from plugins/snmp/snmp__synology rename to plugins/synology/snmp__synology index 20ec1f80..00f98d95 100755 --- a/plugins/snmp/snmp__synology +++ b/plugins/synology/snmp__synology @@ -121,7 +121,7 @@ host_name {hostname} graph_title HDD temperatures on {hostname} graph_vlabel Temperature in °C graph_args --base 1000 -graph_category system +graph_category sensors graph_info HDD temperatures on {hostname}""".format(hostname=self.hostname) for id, name, model, temp in self._get_disks(): @@ -136,7 +136,7 @@ host_name {hostname} graph_title System temperatures of {hostname} graph_vlabel Temperature in °C graph_args --base 1000 -graph_category system +graph_category sensors graph_info System temperature of {hostname} sys_temp.info System temperature sys_temp.label Temperature diff --git a/plugins/snmp/snmp__synology_hddtemp b/plugins/synology/snmp__synology_hddtemp old mode 100644 new mode 100755 similarity index 100% rename from plugins/snmp/snmp__synology_hddtemp rename to plugins/synology/snmp__synology_hddtemp diff --git a/plugins/snmp/snmp__synology_temperature b/plugins/synology/snmp__synology_temperature old mode 100644 new mode 100755 similarity index 100% rename from plugins/snmp/snmp__synology_temperature rename to plugins/synology/snmp__synology_temperature diff --git a/plugins/snmp/snmp__synology_ups b/plugins/synology/snmp__synology_ups old mode 100644 new mode 100755 similarity index 99% rename from plugins/snmp/snmp__synology_ups rename to plugins/synology/snmp__synology_ups index 2ceea9e5..0b1ca5cb --- a/plugins/snmp/snmp__synology_ups +++ b/plugins/synology/snmp__synology_ups @@ -75,7 +75,7 @@ if (defined $ARGV[0] and $ARGV[0] eq "config") { print "graph_title UPS graph_args --base 1000 -l 0 graph_vlabel Status of UPS -graph_category system +graph_category sensors graph_info This graph shows the status of the attached UPS. charge.label Charge charge.info Charge status of battery. diff --git a/plugins/syslog/syslog_ng_stats b/plugins/syslog/syslog_ng_stats old mode 100644 new mode 100755 index 6170c25c..8ec54d99 --- a/plugins/syslog/syslog_ng_stats +++ b/plugins/syslog/syslog_ng_stats @@ -106,7 +106,7 @@ if( exists $ARGV[0] and $ARGV[0] eq "config" ) { print "graph_vlabel count\n"; print "graph_args --base 1000 --lower-limit 0 --rigid\n"; print "graph_info This graph show syslog-ng-ctl stats\n"; - print "graph_category syslog\n"; + print "graph_category system\n"; for my $graph (@gpaths) { # ID my $id = sprintf '%s_%s', diff --git a/plugins/system/auth b/plugins/system/auth index f2d607ad..7edd39ad 100755 --- a/plugins/system/auth +++ b/plugins/system/auth @@ -36,7 +36,7 @@ if [ "$1" = "config" ]; then echo 'graph_title Auth Log Parser' echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel Daily Auth Counters' - echo 'graph_category system' + echo 'graph_category auth' echo 'illegal_user.label Illegal User' echo 'possible_breakin.label Breakin Attempt' echo 'authentication_failure.label Authentication Fail' diff --git a/plugins/system/blockhosts b/plugins/system/blockhosts index c4f9a161..652752e5 100755 --- a/plugins/system/blockhosts +++ b/plugins/system/blockhosts @@ -29,7 +29,7 @@ if [ "$1" = "config" ]; then echo 'graph_title Hosts denied by BlockHosts' echo 'graph_args -l 0' echo 'graph_vlabel denied hosts ' - echo 'graph_category system' + echo 'graph_category security' echo 'HostsDenied.label Hosts denied by BlockHosts' echo 'HostsWatched.label Hosts watched by BlockHosts' exit 0 diff --git a/plugins/system/cpu_linux_multi b/plugins/system/cpu_linux_multi index b8f3dddd..b45e8b47 100755 --- a/plugins/system/cpu_linux_multi +++ b/plugins/system/cpu_linux_multi @@ -34,7 +34,7 @@ # # ######################################################################## # -# multigraph, supersampling, extended cpu informations +# multigraph, supersampling, extended cpu information # # require: mpstat (to actually collect the data) # @@ -142,7 +142,6 @@ my $fields_info = [ sub pidfile() { "$ENV{MUNIN_PLUGSTATE}/munin.$plugin.pid" } sub cachefile() { "$ENV{MUNIN_PLUGSTATE}/munin.$plugin.cache" } -sub graph_section() { "system:cpu" }; sub graph_name() { "cpu_extended_multi_1s" }; sub graph_title() { "CPU usage" }; sub graph_title_all() { "Overall CPU usage" }; @@ -335,7 +334,7 @@ sub run_config() { print < http://nbi.fr/ -# -# Licence : Public Domain -# -#%# family=auto -#%# capabilities=autoconf - -# Auto enable if we have debsecan only -if [ "$1" = "autoconf" ] ; then - if [ -x /usr/bin/debsecan ]; then - echo yes - else - echo no - fi - exit 0 -fi - -# Fail if we don't have debsecan -if [ ! -x /usr/bin/debsecan ]; then - exit 1 -fi - -if [ "$1" = "config" ] ; then - cat < /dev/null > /tmp/debsecan.munin.$$ -high=`grep -c 'high urgency' /tmp/debsecan.munin.$$` -medium=`grep -c 'medium urgency' /tmp/debsecan.munin.$$` -low=`grep -c 'low urgency)' /tmp/debsecan.munin.$$` -other=`grep -c -v -e 'low urgency' -e 'medium urgency' -e 'high urgency' /tmp/debsecan.munin.$$` -cat < (based on disk/log_sizes) + +=head1 LICENSE + +GPLv2 + +=head1 MAGIC MARKERS + + #%# family=manual + +=cut + +# needs shellcheck -x /usr/share/munin/plugins/plugin.sh +# shellcheck source=/usr/share/munin/plugins/plugin.sh +. "$MUNIN_LIBDIR/plugins/plugin.sh" + +NAME=$(echo "$0" | sed 's/.*_//') +TITLE=${title:-File lengths for $NAME} +CATEGORY=${category:-system} + +FILES=${files:-/var/log/messages} +# we want globbing to happen here +# shellcheck disable=SC2116 disable=SC2086 +FILES=$(echo $FILES) + + +if [ "$1" = "config" ] ; then + # shellcheck disable=SC2154 + if [ "${logarithmic}" = "1" ]; then + graph_args="-o" + else + graph_args="-l 0" + fi + cat < + +=head1 LICENSE + +GPLv2 + +=head1 MAGIC MARKERS + + #%# family=manual + +=cut + +# Configuration directives, edit before first use. +BACKUP_DIR=${backup_dir:-/data/backup} +ARCHIVE_PATTERN="${archive_pattern:-*.tar.bz2}" +# How old backups should be considered as non-yound anymore in [days]. +LIFETIME=${lifetime:-2} +# Critical states will be issued when the number of fresh backups archives is below `backup_number`, +# and warnings below `backup_number*lifetime` +CRIT=${backup_number:-1} +WARN=$((CRIT*LIFETIME)) + +# The situation is critical if there are no young files, the backup is down. +case $1 in + config) + cat << EOF +graph_title Fresh (<=${LIFETIME}d) backups archives in ${BACKUP_DIR} +graph_vlabel number +graph_args -l 0 +graph_category backup +freshcount.label number +freshcount.critical ${CRIT}: +freshcount.warning ${WARN}: +EOF + exit 0;; +esac + +printf "freshcount.value " +find "${BACKUP_DIR}" -name "${ARCHIVE_PATTERN}" -a -mtime "-${LIFETIME}" | wc -l +printf "freshcount.extinfo " +du -sh "${BACKUP_DIR}" diff --git a/plugins/system/hugepages b/plugins/system/hugepages index 51f6a6af..78eca8f5 100755 --- a/plugins/system/hugepages +++ b/plugins/system/hugepages @@ -42,7 +42,7 @@ BEGIN { print "graph_title HugePages usage" print "graph_category system" print "graph_info This graph shows the usage of the kernel Huge Pages." - print "graph_order Total Rsvd Free Surp" + print "graph_order Total Rsvd Free Surp Anon" print "Total.label total" print "Total.draw AREA" print "Total.info Size of pool of hugepages ('nr_hugepages')" @@ -55,6 +55,9 @@ BEGIN { print "Surp.label surplus" print "Surp.draw STACK" print "Surp.info Number of hugepages > nr_hugepages, as decided by nr_overcommit_hugepages or when the amount of Huge Pages is reduced while they are in use." + print "Anon.label Transparent" + print "Anon.draw STACK" + print "Anon.info Huge Pages that are in use by the transparent Huge Page allocator khugepaged." CONF=1 } if (ARGC > 1 && ARGV[1] == "autoconf") { @@ -69,6 +72,7 @@ BEGIN { CONF == 1 { if (/Hugepagesize/) { + print "Anon.cdef Anon,",$2,",/" print "graph_vlabel Pages (",$2,"KB/page)" } } diff --git a/plugins/system/irq b/plugins/system/irq index 03017dc9..31f613e4 100755 --- a/plugins/system/irq +++ b/plugins/system/irq @@ -3,7 +3,7 @@ =head1 NAME -irq - Plugin to monitor inerrupts. +irq - Plugin to monitor interrupts. =head1 APPLICABLE SYSTEMS @@ -76,16 +76,16 @@ my $graphs = { 'title' => 'Interrupts per cpu', 'args' => '--base 1000', - 'vlabel' => 'count of IRQ (+) / sIRQ (-) per secund', - 'info' => 'This graph shows interrupts per secund from last update', + 'vlabel' => 'count of IRQ (+) / sIRQ (-) per second', + 'info' => 'This graph shows interrupts per second from last update', 'category' => 'system' }, 'irq_cpu' => { 'title' => 'CPU:cpu: :irq_type:', 'args' => '--base 1000', - 'vlabel' => 'count per secund', - 'info' => 'This graph shows :irq_type: for CPU:cpu: per secund from last update', + 'vlabel' => 'count per second', + 'info' => 'This graph shows :irq_type: for CPU:cpu: per second from last update', 'category' => 'cpu :cpu:' } }; @@ -333,7 +333,7 @@ sub prepare_graphs $gr->{$graph_name}{'graph'}{'category'} = replace($graphs->{'irq_cpu'}{'category'}, ':cpu:', $i); # -- child fields -- my @irq_names = keys %{$IRQi->{'stat'}{$irq_type}{$i}}; - # names splitted for better sorting + # names split for better sorting for my $irq_name (( (sort {int $a <=> int $b} grep{/^\d/} @irq_names), (sort grep{!/(^\d|ERR|MIS)/} @irq_names), diff --git a/plugins/system/multicpu1sec/.gitignore b/plugins/system/multicpu1sec/.gitignore deleted file mode 100644 index 4ff4b96f..00000000 --- a/plugins/system/multicpu1sec/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -/multicpu1sec-c -/multicpu1sec.o -/multicpu1sec.pid -/multicpu1sec.value diff --git a/plugins/system/pagefaults_by_process b/plugins/system/pagefaults_by_process index 379f510e..9396d42c 100755 --- a/plugins/system/pagefaults_by_process +++ b/plugins/system/pagefaults_by_process @@ -80,7 +80,7 @@ if (@ARGV and $ARGV[0] eq "config") graph_title Page faults by Process graph_args --base 1000 graph_vlabel major page faults -graph_category system +graph_category processes graph_info Shows number of major page faults caused by each process name END diff --git a/plugins/system/raminfo b/plugins/system/raminfo index 9b4360a8..d0d0f77d 100755 --- a/plugins/system/raminfo +++ b/plugins/system/raminfo @@ -188,7 +188,7 @@ sub config # print config message and exit. print "graph_args --base 1024 -l 0 --upper-limit " . $h_ramvalue{"Total.value"}. "--rigid" . "\n"; print "graph_vlabel Bytes\n"; print "graph_title RAM usage\n"; - print "graph_category system\n"; + print "graph_category memory\n"; print "graph_info This graph shows what the machine uses RAM for.\n"; print "graph_order "; diff --git a/plugins/system/read_serial_temperature b/plugins/system/read_serial_temperature index 4b432c46..4fef783a 100755 --- a/plugins/system/read_serial_temperature +++ b/plugins/system/read_serial_temperature @@ -39,7 +39,7 @@ elif len(sys.argv) == 2 and sys.argv[1] == "config": print 'graph_title Temperatuur in de serverruimte' print 'graph_vlabel temperature in C' - print 'graph_category System' + print 'graph_category sensors' print 'temperature.label temperature' print 'graph_info Dit is de temperatuur in het rek in de serverruimte B104' print 'graph_scale no' @@ -50,4 +50,4 @@ else: print 'temperature.value %s' % gettemperature() - \ No newline at end of file + diff --git a/plugins/system/total_by_process_ b/plugins/system/total_by_process_ index a2da8fc0..af4b4003 100755 --- a/plugins/system/total_by_process_ +++ b/plugins/system/total_by_process_ @@ -90,7 +90,7 @@ if (@ARGV and $ARGV[1] == "config") { print < + +=head1 LICENSE + +GPLv2 + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=cut + +states="active \ + reloading \ + inactive \ + failed \ + activating \ + deactivating" +autoconf() { + which systemctl >/dev/null && \ + systemctl --state=failed --no-pager --no-legend >/dev/null 2>&1 && echo yes || echo "no (No systemctl or error running it)" +} + +config () { + cat << EOF +graph_title Systemd units state +graph_args -l 0 +graph_category system +graph_scale no +graph_vlabel units +EOF + for state in $states; do + echo "$state.label $state" + echo "$state.draw AREASTACK" + if [ "$state" = "failed" ]; then + echo "$state.warning 0" + echo "$state.critical 10" + fi + done +} + +fetch () { + tmp=$(systemctl --no-pager --no-legend --all | awk '{print $1, $3}') + for state in $states ; do + count=$(echo "$tmp" | grep -c "$state$") + echo "$state.value $count" + extinfo=$(echo "$tmp" | grep "$state$" | cut -d " " -f 1 | tr '\n' ' ') + if [ -n "$extinfo" ]; then + echo "$state.extinfo" "$extinfo" + fi + done +} + +case $1 in + "autoconf") + autoconf + ;; + "config") + config + ;; + *) + fetch + ;; +esac diff --git a/plugins/tarsnap/tarsnap b/plugins/tarsnap/tarsnap index 44e0e677..a5633cff 100755 --- a/plugins/tarsnap/tarsnap +++ b/plugins/tarsnap/tarsnap @@ -37,14 +37,14 @@ case $1 in multigraph tarsnap_total graph_title Tarsnap total data graph_vlabel bytes -graph_category tarsnap +graph_category backup total_size.label Total size total_compressed.label Total size (compressed) multigraph tarsnap_unique graph_title Tarsnap unique data graph_vlabel bytes -graph_category tarsnap +graph_category backup unique_size.label Unique data unique_compressed.label Unique data (compressed) EOM diff --git a/plugins/network/tcp-retransmissions b/plugins/tcp/tcp-retransmissions similarity index 100% rename from plugins/network/tcp-retransmissions rename to plugins/tcp/tcp-retransmissions diff --git a/plugins/network/tcp-states b/plugins/tcp/tcp-states similarity index 100% rename from plugins/network/tcp-states rename to plugins/tcp/tcp-states diff --git a/plugins/teamspeak/teamspeak_user b/plugins/teamspeak/teamspeak_user index 62c0b333..a2490e8d 100755 --- a/plugins/teamspeak/teamspeak_user +++ b/plugins/teamspeak/teamspeak_user @@ -36,7 +36,7 @@ if(exists $ARGV[0] and $ARGV[0] eq "config") { print "graph_title Teamspeak User\n"; print "graph_vlabel Connected Teamspeak Users\n"; - print "graph_category Teamspeak\n"; + print "graph_category voip\n"; print "graph_info This graph shows the number of connected users on a Teamspeak3 server\n"; foreach my $server (@serverids) { @@ -77,4 +77,4 @@ else } $telnet->close; } -exit; \ No newline at end of file +exit; diff --git a/plugins/teamspeak/ts3v2_ b/plugins/teamspeak/ts3v2_ index 5c36eedd..be251358 100755 --- a/plugins/teamspeak/ts3v2_ +++ b/plugins/teamspeak/ts3v2_ @@ -127,7 +127,7 @@ if (exists $ARGV[0] and $ARGV[0] eq "autoconf") { print "graph_title Teamspeak Users ".$name."\n"; print "graph_vlabel Connected Teamspeak Users\n"; - print "graph_category Teamspeak\n"; + print "graph_category voip\n"; print "graph_info This graph shows the number of connected users on a Teamspeak3 server\n"; print "users.label Users\n"; print "users.info Connected users to ".$name."\n"; @@ -138,7 +138,7 @@ if (exists $ARGV[0] and $ARGV[0] eq "autoconf") { #overview mode print "graph_title Teamspeak Users Overview\n"; print "graph_vlabel Connected Teamspeak Users\n"; - print "graph_category Teamspeak\n"; + print "graph_category voip\n"; print "graph_info This graph shows the number of connected users on a Teamspeak3 server\n"; foreach (@num) { @@ -162,7 +162,7 @@ if (exists $ARGV[0] and $ARGV[0] eq "autoconf") { #transfer mode print "graph_title Teamspeak Transfer Overview\n"; print "graph_vlabel Teamspeak Transfer\n"; - print "graph_category Teamspeak\n"; + print "graph_category voip\n"; print "graph_info This graph shows the Teamspeak3 Transfer Overview\n"; print "transfer.label ~ Transfer per second\n"; print "transfer.info Transfer per second over 5 min\n"; diff --git a/plugins/teamspeak/tsuser b/plugins/teamspeak/tsuser index 0cb5b306..30ea2e07 100755 --- a/plugins/teamspeak/tsuser +++ b/plugins/teamspeak/tsuser @@ -45,7 +45,7 @@ if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { print "graph_title Teamspeak User\n"; print "graph_vlabel Connected Teamspeak Users\n"; - print "graph_category Teamspeak\n"; + print "graph_category voip\n"; #print "graph_args --base 1000 -l 0\n"; print "graph_info This graph shows the number of connected users on a teamspeak2 server\n"; foreach my $server (@uports) diff --git a/plugins/snmp/snmp__thecus_fans b/plugins/thecus/snmp__thecus_fans similarity index 100% rename from plugins/snmp/snmp__thecus_fans rename to plugins/thecus/snmp__thecus_fans diff --git a/plugins/thin/thin_memory b/plugins/thin/thin_memory index a496ede4..580ee91e 100755 --- a/plugins/thin/thin_memory +++ b/plugins/thin/thin_memory @@ -77,7 +77,7 @@ case ARGV[0] when "config" puts "graph_title Thin Memory" puts "graph_vlabel RSS" - puts "graph_category Thin" + puts "graph_category webserver" puts "graph_args --base 1024 -l 0" puts "graph_scale yes" puts "graph_info Tracks the size of individual thin processes" diff --git a/plugins/thin/thin_threads b/plugins/thin/thin_threads index bfb09bc7..d559bd08 100755 --- a/plugins/thin/thin_threads +++ b/plugins/thin/thin_threads @@ -81,7 +81,7 @@ case ARGV[0] when "config" puts "graph_title Thin Threads" puts "graph_vlabel Threads" - puts "graph_category Thin" + puts "graph_category webserver" puts "graph_args -l 0" puts "graph_scale yes" puts "graph_info Tracks how many threads per thin processes" diff --git a/plugins/thin/thins_peak_memory b/plugins/thin/thins_peak_memory index 4dc47e5b..3797cc44 100755 --- a/plugins/thin/thins_peak_memory +++ b/plugins/thin/thins_peak_memory @@ -79,7 +79,7 @@ case ARGV[0] when "config" puts "graph_title Thin Peak Memory (High Water Mark)" puts "graph_vlabel HWM" - puts "graph_category Thin" + puts "graph_category webserver" puts "graph_args -l 0" puts "graph_scale yes" puts "graph_info Tracks the peak memory of thin processes, aka High Water Mark." diff --git a/plugins/time/chrony b/plugins/time/chrony deleted file mode 100755 index 96e3ce0f..00000000 --- a/plugins/time/chrony +++ /dev/null @@ -1,68 +0,0 @@ -#!/bin/bash -# -# Script to parse Chrony Tracking Output -# -# Parameters understood: -# -# config (required) -# autoconf (optional - used by munin-config) -# -# $log$ -# Revision 0.1 2008/08/23 13:06:00 joti -# First version only chronyc tracking, autodetection included. -# -# Revision 0.2 2008/10/11 16:09:00 joti -# Added scaling of other values to match with frequency, added more description to fields -# -# Revision 0.3 2014/02/16 zjttoefs -# reduce forking by using awk -# do not limit output precision -# add stratum monitoring -# detect slow/fast time or freqency and adjust sign of value accordingly -# remove commented out code -# -# Magic markers (optional - used by munin-config and installation scripts): -# -#%# family=auto -#%# capabilities=autoconf - -#Modify this to fit other chronyc path -CHRONYC=/usr/bin/chronyc - -#Frequency has extremely higher values than other. Therefore they are fitted by scaling. Adjust the factors here -fieldfactors="1 1000 1 100 100 1000 1000" -#fieldfactors="1 1000000 0.1 100 10 10 10" -fields="stratum systime frequency residualfreq skew rootdelay rootdispersion" -fieldnames="Stratum (=System Time (seconds,x=Frequency (ppm,x=Residual Freq (ppm,x=Skew (ppm,x=Root delay(seconds,x=Root dispersion (seconds,x" - -if [ "$1" = "autoconf" ]; then - if [ -f "$CHRONYC" ]; then - echo yes - exit 0 - else - echo "no (no $CHRONYC)" - exit 1 - fi -fi - -if [ "$1" = "config" ]; then - echo 'graph_title Chrony Tracking Stats' - echo 'graph_args --base 1000 -l 0' - echo 'units (seconds,ppm)' - echo 'graph_category time' - i=0 - for a in $fields ; do - i=$(expr $i + 1); - word=`echo $fieldnames | cut -f $i -d '='`; - factor=`echo $fieldfactors | cut -f $i -d ' '`; - echo "$a.label $word$factor)"; - echo "$a.type GAUGE"; - done - exit 0 -fi - -# remove non-needed output lines, remove labels, rescale and label values while detecting slow/fast keywords -chronyc tracking | sed -e 1d -e 3d -e "s/.*://" | \ - awk -v FAC="$fieldfactors" -v NAM="$fields" \ - 'BEGIN { split(FAC,factors," "); split(NAM,names," "); } - { /slow/ ? SIGN=-1 : SIGN=1 ; print names[NR]".value ",SIGN*$1*factors[NR]}' diff --git a/plugins/network/dns/tinydns b/plugins/tinydns/tinydns similarity index 98% rename from plugins/network/dns/tinydns rename to plugins/tinydns/tinydns index 5d620602..152fc5dc 100755 --- a/plugins/network/dns/tinydns +++ b/plugins/tinydns/tinydns @@ -26,7 +26,7 @@ if [ "$1" = "config" ]; then graph_title tinydns queries graph_args --base 1000 -l 0 graph_vlabel queries/sec -graph_category network +graph_category dns graph_info This graph shows the number of queries that tinydns processed. graph_total Total a.label A diff --git a/plugins/network/dns/tinydns_err b/plugins/tinydns/tinydns_err similarity index 98% rename from plugins/network/dns/tinydns_err rename to plugins/tinydns/tinydns_err index 29b864bc..2f57b768 100755 --- a/plugins/network/dns/tinydns_err +++ b/plugins/tinydns/tinydns_err @@ -26,7 +26,7 @@ if [ "$1" = "config" ]; then graph_title tinydns query errors graph_args --base 1000 -l 0 graph_vlabel queries/sec -graph_category network +graph_category dns graph_info This graph shows the number of queries that tinydns processed. graph_total Total other.label Other RR diff --git a/plugins/tomcat/tomcat_access b/plugins/tomcat/tomcat_access index bdf401d4..f77324b9 100755 --- a/plugins/tomcat/tomcat_access +++ b/plugins/tomcat/tomcat_access @@ -94,7 +94,7 @@ if(exists $ARGV[0] and $ARGV[0] eq "config") { print "graph_title Tomcat accesses\n"; print "graph_args --base 1000\n"; print "graph_vlabel accesses / \${graph_period}\n"; - print "graph_category tomcat\n"; + print "graph_category webserver\n"; print "accesses.label Accesses\n"; print "accesses.type DERIVE\n"; print "accesses.max 1000000\n"; diff --git a/plugins/tor/example-graphs/tor_-1.png b/plugins/tor/example-graphs/tor_-1.png new file mode 100644 index 00000000..d899769f Binary files /dev/null and b/plugins/tor/example-graphs/tor_-1.png differ diff --git a/plugins/tor/example-graphs/tor_-2.png b/plugins/tor/example-graphs/tor_-2.png new file mode 100644 index 00000000..822cc4c6 Binary files /dev/null and b/plugins/tor/example-graphs/tor_-2.png differ diff --git a/plugins/tor/example-graphs/tor_-3.png b/plugins/tor/example-graphs/tor_-3.png new file mode 100644 index 00000000..aaa0cf7d Binary files /dev/null and b/plugins/tor/example-graphs/tor_-3.png differ diff --git a/plugins/tor/example-graphs/tor_-4.png b/plugins/tor/example-graphs/tor_-4.png new file mode 100644 index 00000000..3a3ddc14 Binary files /dev/null and b/plugins/tor/example-graphs/tor_-4.png differ diff --git a/plugins/tor/example-graphs/tor_-5.png b/plugins/tor/example-graphs/tor_-5.png new file mode 100644 index 00000000..d40b7eb8 Binary files /dev/null and b/plugins/tor/example-graphs/tor_-5.png differ diff --git a/plugins/tor/example-graphs/tor_-6.png b/plugins/tor/example-graphs/tor_-6.png new file mode 100644 index 00000000..e1ae6c7e Binary files /dev/null and b/plugins/tor/example-graphs/tor_-6.png differ diff --git a/plugins/tor/example-graphs/tor_-7.png b/plugins/tor/example-graphs/tor_-7.png new file mode 100644 index 00000000..c7f67fa3 Binary files /dev/null and b/plugins/tor/example-graphs/tor_-7.png differ diff --git a/plugins/network/tor/tor-bandwidth-usage b/plugins/tor/tor-bandwidth-usage similarity index 100% rename from plugins/network/tor/tor-bandwidth-usage rename to plugins/tor/tor-bandwidth-usage diff --git a/plugins/network/tor/tor-connections b/plugins/tor/tor-connections similarity index 98% rename from plugins/network/tor/tor-connections rename to plugins/tor/tor-connections index f481d92c..d3ffb26f 100755 --- a/plugins/network/tor/tor-connections +++ b/plugins/tor/tor-connections @@ -17,7 +17,7 @@ if ($cmd == "config") { print "graph_title Connections\n"; print "graph_args -l 0 --base 1000\n"; print "graph_vlabel connections\n"; - print "graph_category Tor\n"; + print "graph_category network\n"; print "graph_info This graph shows the number of Tor OR connections.\n"; print "graph_period second\n"; diff --git a/plugins/network/tor/tor-traffic b/plugins/tor/tor-traffic similarity index 97% rename from plugins/network/tor/tor-traffic rename to plugins/tor/tor-traffic index 8f62e0c2..0b377532 100755 --- a/plugins/network/tor/tor-traffic +++ b/plugins/tor/tor-traffic @@ -17,7 +17,7 @@ if ($cmd == "config") { echo "graph_title Traffic\n"; echo "graph_args --base 1000 \n"; echo "graph_vlabel bits in (-) / out (+) per ${graph_period}\n"; - echo "graph_category Tor\n"; + echo "graph_category network\n"; echo "down.label Download\n"; echo "down.type GAUGE\n"; diff --git a/plugins/tor/tor_ b/plugins/tor/tor_ new file mode 100755 index 00000000..aa022538 --- /dev/null +++ b/plugins/tor/tor_ @@ -0,0 +1,539 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +''' +=head1 NAME +tor_ + +=head1 DESCRIPTION +Wildcard plugin that gathers some metrics from the Tor deamon +https://github.com/daftaupe/munin-tor + +Derived from https://github.com/mweinelt/munin-tor + +This plugin requires the stem library : https://stem.torproject.org/ +This plugin requires the GeoIP library : https://www.maxmind.com for the countries plugin + +Available plugins : +tor_bandwidth # Graph the glabal bandwidth +tor_connections # Graph the number of connexions +tor_countries # Graph the countries represented our connexions +tor_dormant # Graph if tor is dormant or not +tor_flags # Graph the different flags of the relay +tor_routers # Graph the number of routers seen by the relay +tor_traffic # Graph the read/written traffic + +=head2 CONFIGURATION +The default configuration is below +[tor_*] +user toranon # or any other user/group that is running tor +group toranon +env.torcachefile 'munin_tor_country_stats.json' +env.torconnectmethod 'port' +env.torgeoippath "/usr/share/GeoIP/GeoIP.dat" +env.tormaxcountries 15 +env.torport 9051 +env.torsocket '/var/run/tor/control' + +To make it connect through a socket modify this way +[tor_*] +user toranon # or any other user/group that is running tor +group toranon +env.torcachefile 'munin_tor_country_stats.json' +env.torconnectmethod 'socket' +env.torgeoippath "/usr/share/GeoIP/GeoIP.dat" +env.tormaxcountries 15 +env.torport 9051 +env.torsocket '/var/run/tor/control' + +=head1 COPYRIGHT +MIT License + +=head1 AUTHOR +Pierre-Alain TORET +''' + +from __future__ import print_function +import collections +import json +import os +import sys + +try: + import GeoIP + import stem + import stem.control + import stem.connection +except ImportError: + # missing dependencies are reported via "autoconf" + # thus failure is acceptable here + pass + +default_torcachefile = 'munin_tor_country_stats.json' +default_torconnectmethod = 'port' +default_torgeoippath = "/usr/share/GeoIP/GeoIP.dat" +default_tormaxcountries = 15 +default_torport = 9051 +default_torsocket = '/var/run/tor/control' + +#%# family=auto +#%# capabilities=autoconf suggest + + + +class ConnectionError(Exception): + """Error connecting to the controller""" + + +class AuthError(Exception): + """Error authenticating to the controller""" + + +def authenticate(controller): + try: + controller.authenticate() + return + except stem.connection.MissingPassword: + pass + + try: + password = os.environ['torpassword'] + except KeyError: + raise AuthError("Please configure the 'torpassword' " + "environment variable") + + try: + controller.authenticate(password=password) + except stem.connection.PasswordAuthFailed: + print("Authentication failed (incorrect password)", file=sys.stderr) + + +def gen_controller(): + connect_method = os.environ.get('torconnectmethod', default_torconnectmethod) + if connect_method == 'port': + return stem.control.Controller.from_port(port=int(os.environ.get('torport', default_torport))) + elif connect_method == 'socket': + return stem.control.Controller.from_socket_file(path=os.environ.get('torsocket', default_torsocket)) + else: + print("env.torconnectmethod contains an invalid value. Please specify either 'port' or 'socket'.", file=sys.stderr) + sys.exit(1) + + +######################### +# Base Class +######################### + + +class TorPlugin(object): + def __init__(self): + raise NotImplementedError + + def conf(self): + raise NotImplementedError + + @staticmethod + def conf_from_dict(graph, labels): + # header + for key, val in graph.iteritems(): + print('graph_{} {}'.format(key, val)) + # values + for label, attributes in labels.iteritems(): + for key, val in attributes.iteritems(): + print('{}.{} {}'.format(label, key, val)) + + @staticmethod + def autoconf(): + try: + import stem + + except ImportError as e: + print('no (failed to import the required python module "stem": {})'.format(e)) + + try: + import GeoIP + + except ImportError as e: + print('no (failed to import the required python module "GeoIP": {})'.format(e)) + + try: + with gen_controller() as controller: + try: + authenticate(controller) + print('yes') + except stem.connection.AuthenticationFailure as e: + print('no (Authentication failed: {})'.format(e)) + + except stem.connection: + print('no (Connection failed)') + + @staticmethod + def suggest(): + options = ['bandwidth', 'connections', 'countries', 'dormant', 'flags', 'routers', 'traffic'] + + for option in options: + print(option) + + def fetch(self): + raise NotImplementedError + + +########################## +# Child Classes +########################## + + +class TorBandwidth(TorPlugin): + def __init__(self): + pass + + def conf(self): + graph = {'title': 'Tor observed bandwidth', + 'args': '-l 0 --base 1000', + 'vlabel': 'bytes/s', + 'category': 'network', + 'info': 'estimated capacity based on usage in bytes/s'} + labels = {'bandwidth': {'label': 'bandwidth', 'min': 0, 'type': 'GAUGE'}} + + TorPlugin.conf_from_dict(graph, labels) + + def fetch(self): + with gen_controller() as controller: + try: + authenticate(controller) + except stem.connection.AuthenticationFailure as e: + print('Authentication failed ({})'.format(e)) + return + + # Get fingerprint of our own relay to look up the descriptor for. + # In Stem 1.3.0 and later, get_server_descriptor() will fetch the + # relay's own descriptor if no argument is provided, so this will + # no longer be needed. + fingerprint = controller.get_info('fingerprint', None) + if fingerprint is None: + print("Error while reading fingerprint from Tor daemon", file=sys.stderr) + sys.exit(1) + + response = controller.get_server_descriptor(fingerprint, None) + if response is None: + print("Error while getting server descriptor from Tor daemon", file=sys.stderr) + sys.exit(1) + print('bandwidth.value {}'.format(response.observed_bandwidth)) + + +class TorConnections(TorPlugin): + def __init__(self): + pass + + def conf(self): + graph = {'title': 'Tor connections', + 'args': '-l 0 --base 1000', + 'vlabel': 'connections', + 'category': 'network', + 'info': 'OR connections by state'} + labels = {'new': {'label': 'new', 'min': 0, 'max': 25000, 'type': 'GAUGE'}, + 'launched': {'label': 'launched', 'min': 0, 'max': 25000, 'type': 'GAUGE'}, + 'connected': {'label': 'connected', 'min': 0, 'max': 25000, 'type': 'GAUGE'}, + 'failed': {'label': 'failed', 'min': 0, 'max': 25000, 'type': 'GAUGE'}, + 'closed': {'label': 'closed', 'min': 0, 'max': 25000, 'type': 'GAUGE'}} + + TorPlugin.conf_from_dict(graph, labels) + + def fetch(self): + with gen_controller() as controller: + try: + authenticate(controller) + + response = controller.get_info('orconn-status', None) + if response is None: + print("No response from Tor daemon in TorConnection.fetch()", file=sys.stderr) + sys.exit(1) + else: + connections = response.split('\n') + states = dict((state, 0) for state in stem.ORStatus) + for connection in connections: + states[connection.rsplit(None, 1)[-1]] += 1 + for state, count in states.iteritems(): + print('{}.value {}'.format(state.lower(), count)) + except stem.connection.AuthenticationFailure as e: + print('Authentication failed ({})'.format(e)) + + +class TorCountries(TorPlugin): + def __init__(self): + # Configure plugin + self.cache_dir_name = os.environ.get('torcachedir', None) + if self.cache_dir_name is not None: + self.cache_dir_name = os.path.join(self.cache_dir_name, + os.environ.get('torcachefile', default_torcachefile)) + + max_countries = os.environ.get('tormaxcountries', default_tormaxcountries) + self.max_countries = int(max_countries) + + geoip_path = os.environ.get('torgeoippath', default_torgeoippath) + self.geodb = GeoIP.open(geoip_path, GeoIP.GEOIP_MEMORY_CACHE) + + def conf(self): + """Configure plugin""" + + graph = {'title': 'Tor countries', + 'args': '-l 0 --base 1000', + 'vlabel': 'countries', + 'category': 'network', + 'info': 'OR connections by state'} + labels = {} + + countries_num = self.top_countries() + + for c, v in countries_num: + labels[c] = {'label': c, 'min': 0, 'max': 25000, 'type': 'GAUGE'} + + TorPlugin.conf_from_dict(graph, labels) + + # If needed, create cache file at config time + if self.cache_dir_name: + with open(self.cache_dir_name, 'w') as f: + json.dump(countries_num, f) + + def fetch(self): + """Generate metrics""" + + # If possible, read cached data instead of doing the processing twice + try: + with open(self.cache_dir_name) as f: + countries_num = json.load(f) + except: + # Fallback if cache_dir_name is not set, unreadable or any other + # error + countries_num = self.top_countries() + + for c, v in countries_num: + print("%s.value %d" % (c, v)) + + @staticmethod + def _gen_ipaddrs_from_statuses(controller): + """Generate a sequence of ipaddrs for every network status""" + for desc in controller.get_network_statuses(): + ipaddr = desc.address + yield ipaddr + + @staticmethod + def simplify(cn): + """Simplify country name""" + cn = cn.replace(' ', '_') + cn = cn.replace("'", '_') + cn = cn.split(',', 1)[0] + return cn + + def _gen_countries(self, controller): + """Generate a sequence of countries for every built circuit""" + for ipaddr in self._gen_ipaddrs_from_statuses(controller): + country = self.geodb.country_name_by_addr(ipaddr) + if country is None: + yield 'Unknown' + continue + + yield self.simplify(country) + + def top_countries(self): + """Build a list of top countries by number of circuits""" + with gen_controller() as controller: + try: + authenticate(controller) + c = collections.Counter(self._gen_countries(controller)) + return sorted(c.most_common(self.max_countries)) + except stem.connection.AuthenticationFailure as e: + print('Authentication failed ({})'.format(e)) + return [] + + +class TorDormant(TorPlugin): + def __init__(self): + pass + + def conf(self): + graph = {'title': 'Tor dormant', + 'args': '-l 0 --base 1000', + 'vlabel': 'dormant', + 'category': 'network', + 'info': 'Is Tor not building circuits because it is idle?'} + labels = {'dormant': {'label': 'dormant', 'min': 0, 'max': 1, 'type': 'GAUGE'}} + + TorPlugin.conf_from_dict(graph, labels) + + def fetch(self): + with gen_controller() as controller: + try: + authenticate(controller) + + response = controller.get_info('dormant', None) + if response is None: + print("Error while reading dormant state from Tor daemon", file=sys.stderr) + sys.exit(1) + print('dormant.value {}'.format(response)) + except stem.connection.AuthenticationFailure as e: + print('Authentication failed ({})'.format(e)) + + +class TorFlags(TorPlugin): + def __init__(self): + pass + + def conf(self): + graph = {'title': 'Tor relay flags', + 'args': '-l 0 --base 1000', + 'vlabel': 'flags', + 'category': 'network', + 'info': 'Flags active for relay'} + labels = {flag: {'label': flag, 'min': 0, 'max': 1, 'type': 'GAUGE'} for flag in stem.Flag} + + TorPlugin.conf_from_dict(graph, labels) + + def fetch(self): + with gen_controller() as controller: + try: + authenticate(controller) + except stem.connection.AuthenticationFailure as e: + print('Authentication failed ({})'.format(e)) + return + + # Get fingerprint of our own relay to look up the status entry for. + # In Stem 1.3.0 and later, get_network_status() will fetch the + # relay's own status entry if no argument is provided, so this will + # no longer be needed. + fingerprint = controller.get_info('fingerprint', None) + if fingerprint is None: + print("Error while reading fingerprint from Tor daemon", file=sys.stderr) + sys.exit(1) + + response = controller.get_network_status(fingerprint, None) + if response is None: + print("Error while getting server descriptor from Tor daemon", file=sys.stderr) + sys.exit(1) + for flag in stem.Flag: + if flag in response.flags: + print('{}.value 1'.format(flag)) + else: + print('{}.value 0'.format(flag)) + + +class TorRouters(TorPlugin): + def __init__(self): + pass + + def conf(self): + graph = {'title': 'Tor routers', + 'args': '-l 0', + 'vlabel': 'routers', + 'category': 'network', + 'info': 'known Tor onion routers'} + labels = {'routers': {'label': 'routers', 'min': 0, 'type': 'GAUGE'} } + + + TorPlugin.conf_from_dict(graph, labels) + + def fetch(self): + with gen_controller() as controller: + try: + authenticate(controller) + except stem.connection.AuthenticationFailure as e: + print('Authentication failed ({})'.format(e)) + return + + + response = controller.get_info('ns/all', None) + if response is None: + print("Error while reading ns/all from Tor daemon", file=sys.stderr) + sys.exit(1) + else: + routers = response.split('\n') + onr = 0 + for router in routers: + if router[0] == "r": + onr += 1 + + print('routers.value {}'.format(onr)) + + +class TorTraffic(TorPlugin): + def __init__(self): + pass + + def conf(self): + graph = {'title': 'Tor traffic', + 'args': '-l 0 --base 1024', + 'vlabel': 'data', + 'category': 'network', + 'info': 'bytes read/written'} + labels = {'read': {'label': 'read', 'min': 0, 'type': 'DERIVE'}, + 'written': {'label': 'written', 'min': 0, 'type': 'DERIVE'}} + + TorPlugin.conf_from_dict(graph, labels) + + def fetch(self): + with gen_controller() as controller: + try: + authenticate(controller) + except stem.connection.AuthenticationFailure as e: + print('Authentication failed ({})'.format(e)) + return + + response = controller.get_info('traffic/read', None) + if response is None: + print("Error while reading traffic/read from Tor daemon", file=sys.stderr) + sys.exit(1) + + print('read.value {}'.format(response)) + + response = controller.get_info('traffic/written', None) + if response is None: + print("Error while reading traffic/write from Tor daemon", file=sys.stderr) + sys.exit(1) + print('written.value {}'.format(response)) + + +########################## +# Main +########################## + + +def main(): + if len(sys.argv) > 1: + param = sys.argv[1].lower() + else: + param = 'fetch' + + if param == 'autoconf': + TorPlugin.autoconf() + sys.exit() + elif param == 'suggest': + TorPlugin.suggest() + sys.exit() + else: + # detect data provider + if __file__.endswith('_bandwidth'): + provider = TorBandwidth() + elif __file__.endswith('_connections'): + provider = TorConnections() + elif __file__.endswith('_countries'): + provider = TorCountries() + elif __file__.endswith('_dormant'): + provider = TorDormant() + elif __file__.endswith('_flags'): + provider = TorFlags() + elif __file__.endswith('_routers'): + provider = TorRouters() + elif __file__.endswith('_traffic'): + provider = TorTraffic() + else: + print('Unknown plugin name, try "suggest" for a list of possible ones.', file=sys.stderr) + sys.exit(1) + + if param == 'config': + provider.conf() + elif param == 'fetch': + provider.fetch() + else: + print('Unknown parameter "{}"'.format(param), file=sys.stderr) + sys.exit(1) + +if __name__ == '__main__': + main() diff --git a/plugins/network/tor/tor_routers b/plugins/tor/tor_routers similarity index 99% rename from plugins/network/tor/tor_routers rename to plugins/tor/tor_routers index 27bb7b21..4709bfd4 100755 --- a/plugins/network/tor/tor_routers +++ b/plugins/tor/tor_routers @@ -99,7 +99,7 @@ if ($ARGV[0] and $ARGV[0] eq "config") { print "graph_title Routers\n"; print "graph_args -l 0\n"; print "graph_vlabel routers\n"; - print "graph_category Tor\n"; + print "graph_category network\n"; print "graph_info This graph shows the number of known Tor ORs.\n"; print "ors.label routers\n"; diff --git a/plugins/network/tor/tor_traffic b/plugins/tor/tor_traffic similarity index 100% rename from plugins/network/tor/tor_traffic rename to plugins/tor/tor_traffic diff --git a/plugins/torrent/deluge_ b/plugins/torrent/deluge_ new file mode 100755 index 00000000..6812ae21 --- /dev/null +++ b/plugins/torrent/deluge_ @@ -0,0 +1,400 @@ +#! /usr/bin/env python2 + +"""=cut +=head1 NAME + +deluge_ - Munin wildcard plugin to monitor Deluge torrent client + +=head1 REQUIREMENTS + + - Python2.5+ (Deluge itself won't work with python3) + - Deluge + +This plugin also uses + - deluge.ui.client + - deluge.log + - twisted +These modules are required by Deluge itself. + +=head1 INSTALLATION + +This plugin has 3 modes : + - connections : monitors the number of connections + - bandwidth : monitors the bandwidth (up, up overhead, down, down overhead) + - states : monitors the torrents' states + +To use one of these modes, link the this plugin as 'deluge_' +For example : +ln -s /path/to/deluge_ /etc/munin/plugins/deluge_connections + +=head1 CONFIGURATION + +Use your "/etc/munin/plugin-conf.d/munin-node" to configure this plugin. +You must at least add : + [deluge_*] + user + env.HOME + +By default, this plugin will try to access the deluge daemon with the following +settings : + host 127.0.0.1 + port 58846 + no username + no password + +You can change these settings in "plugin-conf.d/munin-node" : + [deluge_*] + user + env.HOME + env.host 127.0.0.1 + env.port 58846 + env.username user + env.password pass + +By default, deluge configuration files will be searched under $XDG_CONFIG_HOME, +which is by default set to $HOME/.config +Setting env.HOME allows this default to work. However, you can also explicitly +set the env.XDG_CONFIG_HOME if needed. + +=head1 INTERPRETATION + +=head2 connections + +In the "connections" mode, this plugin shows a graph with the number of +connections + +=head2 bandwidth + +In the "bandwidth" mode, this plugin show graphs for the download and upload +bandwidths. +Each of them has "payload" and "overhead" value. + - with positive values : the upload values + - with negative values : the download values + +=head2 states + +In the "states" mode, this plugin shows the number of torrents in each state : +Downloading, Seeding, Paused, Error, Queued, Checking, Other + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf suggest + +=head1 VERSION + +1.0.0 + +=head1 AUTHOR + +Neraud (https://github.com/Neraud) + +=head1 LICENSE + +GPLv2 + +=cut""" + + +from __future__ import print_function + +import logging +import os +import string +import sys + + +try: + from deluge.log import setupLogger + from deluge.ui.client import client + from twisted.internet import reactor, defer + setupLogger() +except (ImportError, NameError): + successful_import = False +else: + successful_import = True + + +plugin_version = "1.0.0" + +log = logging.getLogger("delugeStats") +log.setLevel(logging.WARNING) + +conf = { + 'host': os.getenv('host', '127.0.0.1'), + 'port': int(os.getenv('port', '58846')), + 'username': os.getenv('username', ''), + 'password': os.getenv('password', '') +} + +names_for_munin = { + 'numConnections': 'numConnections', + 'payloadUploadRate': 'payloadUploadRate', + 'overheadUploadRate': 'overheadUploadRate', + 'payloadDownloadRate': 'payloadDownloadRate', + 'overheadDownloadRate': 'overheadDownloadRate', + 'state.Seeding': 'seeding', + 'state.Downloading': 'downloading', + 'state.Paused': 'paused', + 'state.Error': 'error', + 'state.Queued': 'queued', + 'state.Checking': 'checking', + 'state.Other': 'other' +} + +torrent_states = ["Downloading", + "Seeding", + "Paused", + "Error", + "Queued", + "Checking", + "Other"] + +modes = ["bandwidth", "connections", "states"] + + +class StatClient: + + def __init__(self, conf, mode): + self.conf = conf + self.mode = mode + self.connected = False + + def end_session(self, msg): + log.debug("end_session : %s", msg) + + if self.connected: + log.debug("Disconnecting") + client.disconnect() + + reactor.stop() + + def fetch_info(self): + log.debug("Connecting to %s:%d ...", + self.conf['host'], self.conf['port']) + client.connect( + self.conf['host'], + self.conf['port'], + self.conf['username'], + self.conf['password']).addCallbacks( + self.on_connect_success, + self.end_session, + errbackArgs=("Connection failed: check settings and try again.")) + reactor.run() + + def on_connect_success(self, result): + log.debug("Connection was successful") + self.connected = True + + if self.mode == "connections": + log.debug("Calling get_num_connections") + client.core.get_num_connections().addCallbacks( + self.on_num_connections, + self.end_session, errbackArgs=("get_num_connections failed")) + elif self.mode == "bandwidth": + log.debug("Calling get_session_status") + interesting_status = [ + 'upload_rate', 'payload_upload_rate', + 'download_rate', 'payload_download_rate'] + client.core.get_session_status(interesting_status).addCallbacks( + self.on_bandwidth, + self.end_session, + errbackArgs=("get_session_status failed")) + elif self.mode == "states": + log.debug("Calling get_session_state") + client.core.get_session_state().addCallbacks( + self.on_session_state, + self.end_session, + errbackArgs=("get_session_state failed")) + + def on_num_connections(self, num_connections): + log.debug("Got num_connections from the daemon : %s", num_connections) + print("{0}.value {1}".format( + names_for_munin["numConnections"], num_connections)) + self.end_session("Done") + + def on_bandwidth(self, values): + log.debug("Got bandwidth info from the daemon : %s", values) + + download_rate = values['download_rate'] + payload_download_rate = values['payload_download_rate'] + overhead_download_rate = download_rate - payload_download_rate + upload_rate = values['upload_rate'] + payload_upload_rate = values['payload_upload_rate'] + overhead_upload_rate = upload_rate - payload_upload_rate + + print("{0}.value {1}".format( + names_for_munin["payloadDownloadRate"], payload_download_rate)) + print("{0}.value {1}".format( + names_for_munin["overheadDownloadRate"], overhead_download_rate)) + print("{0}.value {1}".format( + names_for_munin["payloadUploadRate"], payload_upload_rate)) + print("{0}.value {1}".format( + names_for_munin["overheadUploadRate"], overhead_upload_rate)) + self.end_session("Done") + + def on_session_state(self, torrent_ids): + log.debug("Got session state from the daemon") + + self.states = {} + for state_name in torrent_states: + self.states[state_name] = 0 + + deferred_list = [] + for torrent_id in torrent_ids: + log.debug(" - TorrentId : %s", torrent_id) + d = client.core.get_torrent_status(torrent_id, ['state']) + d.addCallback(self.on_one_torrent_info, torrent_id) + d.addErrback(self.on_one_torrent_info_failed, torrent_id) + deferred_list.append(d) + + defer.DeferredList(deferred_list).addCallback( + self.on_all_torrent_info_fetched) + + def on_one_torrent_info_failed(self, torrent_id): + log.debug("Failed fetching torrent info %s", torrent_id) + self.state["Error"] = self.state["Error"] + 1 + + def on_one_torrent_info(self, value, torrent_id): + log.debug("Got torrent info : %s -> %s", torrent_id, value) + state = value.get("state", "Error") + + if state not in self.states: + log.warn("State '%s' is unknown !", state) + state = "Other" + + self.states[state] += 1 + + def on_all_torrent_info_fetched(self, res): + log.debug("on_all_torrent_info_fetched : %s", self.states) + + for state in self.states: + print("{0}.value {1}".format( + names_for_munin["state." + state], self.states[state])) + + self.end_session("Done") + + +def get_mode(): + script_name = os.path.basename(sys.argv[0]) + mode = script_name[string.rindex(script_name, '_') + 1:] + + log.debug("Mode : %s", mode) + + if mode not in modes: + log.error("Unknown mode '%s'", mode) + log.info("Available modes are : %s", modes) + sys.exit(1) + + return mode + + +def print_config(mode): + if mode == "connections": + print("graph_title Number of connections") + print("graph_args --base 1000 -l 0") + print("graph_vlabel connections") + print("graph_scale yes") + print("graph_category filetransfer") + print( + "graph_info This graph shows the number of connections used by Deluge Torrent") + print(names_for_munin["numConnections"] + ".label connections") + print(names_for_munin["numConnections"] + ".min 0") + print(names_for_munin["numConnections"] + + ".info The number of connections used by Deluge Torrent") + elif mode == "bandwidth": + print("graph_title Bandwidth usage") + print("graph_order payloadDownloadRate overheadDownloadRate payloadUploadRate " + "overheadUploadRate") + print("graph_args --base 1024 -r") + print("graph_vlabel bytes/s : down(-) and up(+)") + print("graph_scale yes") + print("graph_info This graph shows the bandwidth used by Deluge Torrent") + print("graph_category filetransfer") + print("graph_period second") + + print("payloadDownloadRate.label payload") + print("payloadDownloadRate.draw AREA") + print("payloadDownloadRate.min 0") + print("payloadDownloadRate.graph no") + print("payloadDownloadRate.info Bandwidth used to download / upload torrents") + + print("overheadDownloadRate.label overhead") + print("overheadDownloadRate.draw STACK") + print("overheadDownloadRate.min 0") + print("overheadDownloadRate.graph no") + print("overheadDownloadRate.info Bandwidth 'lost' due to overhead while downloading and " + "uploading torrents") + + print("payloadUploadRate.label payload") + print("payloadUploadRate.draw AREA") + print("payloadUploadRate.min 0") + print("payloadUploadRate.negative payloadDownloadRate") + print("payloadUploadRate.info Bandwidth used to upload torrents") + + print("overheadUploadRate.label overhead") + print("overheadUploadRate.draw STACK") + print("overheadUploadRate.min 0") + print("overheadUploadRate.negative overheadDownloadRate") + print("overheadUploadRate.info Bandwidth 'lost' due to overhead while downloading and " + "uploading torrents") + elif mode == "states": + print("graph_title Torrents states") + + graph_order = " ".join( + [names_for_munin["state.{}".format(name)] for name in torrent_states]) + + print("graph_order " + graph_order) + print("graph_args --base 1000 -r --lower-limit 0") + print("graph_vlabel number of torrents") + print("graph_scale yes") + print("graph_info This graph shows the states of the torrents in Deluge Torrent") + print("graph_category filetransfer") + print("graph_period second") + + for state_name in torrent_states: + print(names_for_munin["state." + + state_name] + ".label " + state_name) + print(names_for_munin["state." + state_name] + ".draw AREASTACK") + print(names_for_munin["state." + state_name] + ".type GAUGE") + print(names_for_munin["state." + state_name] + ".min 0") + print(names_for_munin["state." + state_name] + + ".info Number of torrents in the '" + state_name + "' state") + + +def fetch_info(mode): + if not successful_import: + print('Missing imports, cannot run !', file=sys.stderr) + sys.exit(1) + + log.debug("Launching tests") + c = StatClient(conf, mode) + c.fetch_info() + + +# Parse arguments +if len(sys.argv) > 1: + action = sys.argv[1] + if action == "config": + print_config(get_mode()) + sys.exit(0) + elif action == "autoconf": + if not successful_import: + print('no (required modules not found)') + sys.exit(0) + print('yes') + elif action == "suggest": + for mode in modes: + print(mode) + sys.exit(0) + elif action == "version": + print('Deluge Torrent Munin plugin, version {0}'.format( + plugin_version)) + sys.exit(0) + elif action: + log.warn("Unknown argument '%s'", action) + sys.exit(1) + else: + fetch_info(get_mode()) +else: + fetch_info(get_mode()) diff --git a/plugins/trafic_ro/trafic_ro_24h b/plugins/trafic_ro/trafic_ro_24h index 9200fc5f..fe49fcf2 100755 --- a/plugins/trafic_ro/trafic_ro_24h +++ b/plugins/trafic_ro/trafic_ro_24h @@ -5,7 +5,7 @@ if [ "$1" = "config" ]; then echo graph_title $V 24h visitors for $RID echo 'graph_args --base 1000' echo 'graph_vlabel vizitatori_ultimele_24_ore' - echo 'graph_category Trafic_ro' + echo 'graph_category other' echo 'graph_info 24h visitors.' echo "24h_visitors.label $RID" echo "24h_visitors.info 24H visitors for $RID" diff --git a/plugins/transmission_ratios/tr_ratios b/plugins/transmission_ratios/tr_ratios new file mode 100755 index 00000000..d9219a6d --- /dev/null +++ b/plugins/transmission_ratios/tr_ratios @@ -0,0 +1,91 @@ +#!/bin/sh +# -*- sh -*- + +: <<=cut + +=head1 NAME + +tr_ratios - monitor transfer ratios of the "transmission" bittorent program + +=head1 APPLICABLE SYSTEMS + +Any system with "transmission" installed and a transmission daemon running. + +=head1 CONFIGURATION + +Maybe you need to configure access credentials and connection settings: + + [tr_ratios] + env.host localhost + env.port 9091 + env.username alice + env.password secret + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=head1 AUTHOR + +unspecified + +=head1 LICENSE + +unspecified + +=cut + + +CONNECTION_ARG="${host:-localhost}:${port:-9091}" +USERNAME="${username:-}" +PASSWORD="${password:-}" + + +# return a space separated list of transmissions with the following columns: +# * fieldname +# * ratio (in percent) +# * name of the transmissions +request_transmission_stats() { + if [ -n "$USERNAME$PASSWORD" ]; then + transmission-remote "$CONNECTION_ARG" --auth "$USERNAME:$PASSWORD" --list + else + transmission-remote "$CONNECTION_ARG" --list + fi | awk ' + BEGIN { FIELDWIDTHS = "7 4 13 10 7 9 7 13 40" } + NR > 1 { + split($1,torrentid," ") + # remove "*" from the ID of stopped transmissions + sub(/\*/, "", torrentid[1]) + if (torrentid[1] != "Sum:") { + split($7,ratio," ") + ratio[1] = ratio[1] * 100 + print "ID" torrentid[1], ratio[1], $9 + } + }' +} + + +if [ "$1" = "autoconf" ]; then + if [ -n "$(request_transmission_stats 2>/dev/null)" ]; then + echo "yes" + else + if which transmission-remote >/dev/null; then + echo "no (failed to connect to daemon)" + else + echo "no (missing 'transmission-remote' program)" + fi + fi + exit 0 +fi + +if [ "$1" = "config" ]; then + echo "graph_title Transmission seed ratios" + echo "graph_vlabel Seed ratio %" + echo "graph_category filetransfer" + echo "graph_info This plugin shows your transmission ratios per torrent" + request_transmission_stats | awk '{print $1 ".label " $3 }' | iconv -f utf-8 -t ascii//translit + exit 0 +fi + +request_transmission_stats | awk '{print $1 ".value " $2 }' diff --git a/plugins/tv/hdhomerun_ b/plugins/tv/hdhomerun_ index d6267422..2fafe0ff 100755 --- a/plugins/tv/hdhomerun_ +++ b/plugins/tv/hdhomerun_ @@ -50,7 +50,7 @@ if [ "$1" = "config" ] ; then echo "graph_args --upper-limit 100 -l 0" echo "graph_vlabel %" echo "graph_scale no" - echo "graph_category tv" + echo "graph_category sensors" echo "ss.label Signal Strength" echo "snq.label Signal Quality" echo "seq.label Symbol Quality" diff --git a/plugins/nutcracker/nutcracker_requests_ b/plugins/twemproxy/nutcracker_requests_ similarity index 97% rename from plugins/nutcracker/nutcracker_requests_ rename to plugins/twemproxy/nutcracker_requests_ index 2ed5a774..bc06d654 100755 --- a/plugins/nutcracker/nutcracker_requests_ +++ b/plugins/twemproxy/nutcracker_requests_ @@ -48,7 +48,7 @@ def process_data(): def process_config(): print "graph_title Nutcracker requests/s" - print "graph_category nutcracker" + print "graph_category other" print "graph_vlabel requests/s" data = get_stats(); diff --git a/plugins/ubuntu/apt_ubuntu b/plugins/ubuntu/apt_ubuntu index a54e1008..cf30c0c5 100755 --- a/plugins/ubuntu/apt_ubuntu +++ b/plugins/ubuntu/apt_ubuntu @@ -21,12 +21,15 @@ # Magic markers - optional - used by installation scripts and # munin-config: # -#%# capabilities=autoconf -#%# family=contrib +# #%# capabilities=autoconf +# #%# family=contrib + +import os +import sys +import warnings ########################################################### -category = 'security' # 'upgrades' -title = 'Upgradable packages' # 'Upgradeable packages' +title = 'Upgradable packages' # 'Upgradeable packages' vlabel = 'Total packages' other = 'other' total = 'total' @@ -38,12 +41,9 @@ origins = ['Ubuntu'] critical = 1 ########################################################### -import os -import sys -import warnings - warnings.filterwarnings('ignore', 'apt API not stable yet', FutureWarning) + def autoconf(): if os.path.exists('/etc/lsb-release'): for line in open('/etc/lsb-release'): @@ -51,60 +51,60 @@ def autoconf(): try: import apt except ImportError: - print 'no (python-apt not installed)' - sys.exit(1) + print('no (python-apt not installed)') + sys.exit(0) cache = apt.Cache() - if not cache.has_key('update-notifier-common'): - print 'no (update-notifier-common not found)' - sys.exit(1) + if 'update-notifier-common' not in cache: + print('no (update-notifier-common not found)') + sys.exit(0) if not cache['update-notifier-common'].isInstalled: - print 'no (update-notifier-common not installed)' - sys.exit(1) + print('no (update-notifier-common not installed)') + sys.exit(0) if not os.path.exists('/etc/apt/apt.conf.d/10periodic'): - print 'no (/etc/apt/apt.conf.d/10periodic not found)' + print('no (/etc/apt/apt.conf.d/10periodic not found)') sys.exit(1) for line in open('/etc/apt/apt.conf.d/10periodic'): if line.strip() == 'APT::Periodic::Update-Package-Lists "1";': - print 'yes' + print('yes') sys.exit(0) - print 'no (APT::Periodic::Update-Package-Lists not "1")' - sys.exit(1) - print 'no' - sys.exit(1) - -def config(): - print 'graph_category security' - print 'graph_title %s' % (title) - #print 'graph_total %s' % (total) - print 'graph_vlabel %s' % (vlabel) - for i, archive in enumerate(archives + [other]): - if len(colour) > i: - print '%s.colour %s' % (archive, colour[i]) - if i < critical: - print '%s.critical 0:0' % (archive) - if i == 0: - print '%s.draw AREA' % (archive) - else: - print '%s.draw STACK' % (archive) - print '%s.label %s' % (archive, archive) - if i + 1 > critical: - print '%s.warning 0:0' % (archive) - print 'total.colour 000000' - print 'total.draw LINE1' - print 'total.label %s' % (total) + print('no (APT::Periodic::Update-Package-Lists not "1")') + sys.exit(0) + print('no (missing /etc/lsb-release file)') sys.exit(0) + +def config(): + print('graph_category security') + print('graph_title %s' % (title)) + print('graph_vlabel %s' % (vlabel)) + for i, archive in enumerate(archives + [other]): + if len(colour) > i: + print('%s.colour %s' % (archive, colour[i])) + if i < critical: + print('%s.critical 0:0' % (archive)) + if i == 0: + print('%s.draw AREA' % (archive)) + else: + print('%s.draw STACK' % (archive)) + print('%s.label %s' % (archive, archive)) + if i + 1 > critical: + print('%s.warning 0:0' % (archive)) + print('total.colour 000000') + print('total.draw LINE1') + print('total.label %s' % (total)) + sys.exit(0) + + def check_origin(pkg): - #print 'Checking: %s (%s)' % (pkg.name, map(str, pkg.candidateOrigin)) if pkg.candidate.origins: for archive in archives: for origin in pkg.candidate.origins: - #a = origin.archive.rpartition('-')[2] a = origin.archive.split('-')[origin.archive.count('-')] - if a == archive and origin.origin in origins: + if (a == archive) and (origin.origin in origins): return a return other + if len(sys.argv) > 1: if sys.argv[1] == 'autoconf': autoconf() @@ -116,19 +116,22 @@ if len(sys.argv) > 1: try: import apt + import apt_pkg except ImportError: - print "The module 'apt' is currently not installed. You can install it by typing:\nsudo apt-get install python-apt\nImportError: No module named apt" + print("The module 'apt' is currently not installed. You can install it by typing:\n" + "sudo apt-get install python-apt\nImportError: No module named apt") sys.exit(1) + pkgs = {} total = 0 for pkg in apt.Cache(): - if pkg.is_upgradable: + if (pkg.is_upgradable) and (pkg._pkg.selected_state != apt_pkg.SELSTATE_HOLD): a = check_origin(pkg) pkgs[a] = pkgs.get(a, 0) + 1 total += 1 for archive in archives + [other]: - print '%s.value %s' % (archive, pkgs.pop(archive, 0)) + print('%s.value %s' % (archive, pkgs.pop(archive, 0))) -print 'total.value %s' % (total) +print('total.value %s' % (total)) diff --git a/plugins/ubuntu/ubuntu-mirrors b/plugins/ubuntu/ubuntu-mirrors index 9e5efea1..b9897280 100755 --- a/plugins/ubuntu/ubuntu-mirrors +++ b/plugins/ubuntu/ubuntu-mirrors @@ -88,7 +88,7 @@ try break; case 'config': echo "graph_title Ubuntu Mirrors in ".COUNTRY."\n"; - echo "graph_category Ubuntu\n"; + echo "graph_category other\n"; echo "graph_args --base 1000 -l 0\n"; echo "graph_scale no\n"; echo "graph_vlabel Bandwidth / Mirrors\n"; diff --git a/plugins/udp/udp-statistics b/plugins/udp/udp-statistics new file mode 100755 index 00000000..aad671e2 --- /dev/null +++ b/plugins/udp/udp-statistics @@ -0,0 +1,65 @@ +#!/usr/bin/perl -w + +if ( $ARGV[0] ) { + + if ( $ARGV[0] eq 'autoconf' ) { + if ( -r '/bin/netstat') { + print "yes\n"; + exit 0; + } + print "no\n"; + exit 0; + + } elsif ( $ARGV[0] eq 'config' ) { + print </dev/null | tr \"\n\" \" \"" | getline pids + user_cpu = 0 + split(pids, pid_array) + # summarize the cpu usage of this usage + for (pid_index in pid_array) { + pid = pid_array[pid_index] + user_cpu += CPU_PER_PID[pid] + delete CPU_PER_PID[pid] + } + print user, user_cpu + } + # add all remaining cpu usages into "others" + others_sum = 0 + for (other_usage in CPU_PER_PID) others_sum+=CPU_PER_PID[other_usage] + print "'"$OTHER_FIELD"'", others_sum; + }' | while read -r user count; do + # apply fieldname cleanup + echo "$(clean_fieldname "$user").value $count" + done diff --git a/plugins/system/membyuser b/plugins/user/membyuser similarity index 97% rename from plugins/system/membyuser rename to plugins/user/membyuser index 9b869cca..25b8b3fb 100755 --- a/plugins/system/membyuser +++ b/plugins/user/membyuser @@ -19,7 +19,7 @@ fi if [ "$1" = "config" ]; then echo "graph_args --base 1000 -r --lower-limit 0" echo "graph_title Memory usage, by user" - echo "graph_category system" + echo "graph_category memory" echo "graph_info This graph shows memory usage, for monitored users." echo "graph_vlabel KB" echo "graph_scale no" diff --git a/plugins/processes/multipsu b/plugins/user/multipsu similarity index 100% rename from plugins/processes/multipsu rename to plugins/user/multipsu diff --git a/plugins/user/proc_mem_by_user b/plugins/user/proc_mem_by_user new file mode 100755 index 00000000..6b1026f8 --- /dev/null +++ b/plugins/user/proc_mem_by_user @@ -0,0 +1,66 @@ +#!/bin/sh +# +# (c) 2015, Raphaël Droz +# (c) 2014, Gilles Fauvie +# Based on the 'du_multidirs' plugin, written by Christian Kujau +# +# Configure it by using the processes env var, i.e.: +# +# WARNING: SELINUX can block this plugin +# +# [proc_mem_by_user] +# env.users munin-node jprod +# +# see bug: +# http://munin-monitoring.org/ticket/921 +# /usr/share/munin/plugins/plugin.sh (clean_fieldname()) + +. "$MUNIN_LIBDIR/plugins/plugin.sh" +users=${users:-munin-node} + + +if [ "$1" = autoconf ]; then + ok=1 + [ -z "$users" ] && ok=0 + + for user in $users; do + ps u -U "$user" 1> /dev/null 2>&1 || ok=0 + done + + if [ $ok = 1 ]; then echo yes + else echo no; fi + exit 0 +fi + +if [ "$1" = config ]; then + cat < 1 { sum += $6 }; END { print sum * 1024 }') + echo "$munin_safe_name.value ${sum:-U}" +done diff --git a/plugins/system/system_users b/plugins/user/system_users similarity index 100% rename from plugins/system/system_users rename to plugins/user/system_users diff --git a/plugins/other/uustat b/plugins/uucp/uustat similarity index 94% rename from plugins/other/uustat rename to plugins/uucp/uustat index 8002d7e3..76bd6de7 100755 --- a/plugins/other/uustat +++ b/plugins/uucp/uustat @@ -12,7 +12,6 @@ # # Environment # exclude: space separated list hosts to exclude. -# category: category of this plugin, default 'postfix' # host.warning: Warning packages threshold, default none. # host.critical: Critical packages threshold, default none. # @@ -88,13 +87,12 @@ if (@ARGV > 0 && $ARGV[0] eq 'autoconf') { } if (@ARGV > 0 && $ARGV[0] eq 'config') { - my $category = $ENV{'category'} || 'postfix'; print "graph_title UUCP usage \n"; print "graph_args -l 0\n"; print "graph_vlabel packages\n"; print "graph_scale no\n"; - printf "graph_category %s\n", $category; + print "graph_category mail\n"; foreach my $host (hosts) { next if host_excluded $host; diff --git a/plugins/uwsgi/uwsgi_ b/plugins/uwsgi/uwsgi_ old mode 100644 new mode 100755 index fb56f430..c0fb48eb --- a/plugins/uwsgi/uwsgi_ +++ b/plugins/uwsgi/uwsgi_ @@ -21,13 +21,13 @@ if [ "$mode" = "memory" ]; then if [ "$1" = "config" ]; then echo "graph_title Total uWSGI Memory" echo "graph_vlabel Total RAM" - echo "graph_category uWSGI" + echo "graph_category appserver" echo "graph_args --base 1024" echo "ram.label Total RAM" exit 0 else - memory_array=(`ps auwx | grep "uwsgi_python" | grep -v grep | awk '{print $6 }'`) + memory_array=(`ps auwx | grep -i "uwsgi" | grep -v grep | awk '{print $6 }'`) for i in "${memory_array[@]}" do @@ -42,11 +42,11 @@ elif [ "$mode" = "processes" ]; then if [ "$1" = "config" ]; then echo "graph_title uWSGI Processes" echo "graph_vlabel Processes" - echo "graph_category uWSGI" + echo "graph_category appserver" echo "processes.label active processes" else echo -n "processes.value " - ps awwwux | grep 'uwsgi_python' | grep -v grep | wc -l + ps awwwux | grep -i 'uwsgi' | grep -v grep | wc -l exit 0 fi @@ -55,13 +55,13 @@ elif [ "$mode" = "average" ]; then echo 'graph_title uWSGI Average Process Size' echo 'graph_args --base 1024 -l 0 ' echo 'graph_vlabel Average Process Size' - echo 'graph_category uWSGI' + echo 'graph_category appserver' echo 'uwsgi_average.label Average Process Size' echo 'uwsgi_average.draw LINE2' echo 'uwsgi_average.info The average process size for uWSGI' else echo -n "uwsgi_average.value " - ps awwwux | grep 'uwsgi_python' | grep -v grep | awk '{total_mem = $6 * 1024 + total_mem; total_proc++} END{printf("%d\n", total_mem / total_proc)}' + ps awwwux | grep -i 'uwsgi' | grep -v grep | awk '{total_mem = $6 * 1024 + total_mem; total_proc++} END{printf("%d\n", total_mem / total_proc)}' exit 0 fi diff --git a/plugins/varnish4/README.md b/plugins/varnish/README-varnish4.md similarity index 100% rename from plugins/varnish4/README.md rename to plugins/varnish/README-varnish4.md diff --git a/plugins/varnish/varnish2_ b/plugins/varnish/varnish2_ index 65a96d09..0d3ec1b3 100755 --- a/plugins/varnish/varnish2_ +++ b/plugins/varnish/varnish2_ @@ -50,7 +50,7 @@ if ($config) { print "graph_title Varnish $grepfor usage\n"; print "graph_args --base 1000\n"; print "graph_vlabel Activity / \${graph_period}\n"; - print "graph_category Varnish\n"; + print "graph_category webserver\n"; } $i = 0; diff --git a/plugins/varnish4/varnish4_ b/plugins/varnish/varnish4_ old mode 100644 new mode 100755 similarity index 99% rename from plugins/varnish4/varnish4_ rename to plugins/varnish/varnish4_ index fa400c1d..ec96075c --- a/plugins/varnish4/varnish4_ +++ b/plugins/varnish/varnish4_ @@ -1076,12 +1076,12 @@ sub get_config } my %values = %{$ASPECTS{$graph}{'values'}}; - print "graph_category Varnish\n"; + print "graph_category webserver\n"; foreach my $field (@graph_parameters) { print_if_exist(\%ASPECTS,$graph,$field,"graph_$field"); } - foreach my $value (keys %values) { + foreach my $value (sort keys %values) { # Just print the description/type if it's a dynamic # counter. It'll be silent if it isn't. if(print_dynamic($value,'.label',('description','type','ident'))) { diff --git a/plugins/varnish/varnish_devicedetect b/plugins/varnish/varnish_devicedetect index 52e1bbcb..ddcbb808 100755 --- a/plugins/varnish/varnish_devicedetect +++ b/plugins/varnish/varnish_devicedetect @@ -63,7 +63,7 @@ export devices print_config() { printf "graph_title Varnish device detection\n" printf "graph_vlabel percent\n" - printf "graph_category varnish\n" + printf "graph_category webserver\n" printf "graph_args --rigid --lower-limit 0 --upper-limit 100\n" for device in $devices; do diff --git a/plugins/forums/vbulletin4_users b/plugins/vbulletin/vbulletin4_users old mode 100644 new mode 100755 similarity index 98% rename from plugins/forums/vbulletin4_users rename to plugins/vbulletin/vbulletin4_users index 5f6ee7e5..ead4ebb8 --- a/plugins/forums/vbulletin4_users +++ b/plugins/vbulletin/vbulletin4_users @@ -60,7 +60,7 @@ if ( defined $ARGV[0] and $ARGV[0] eq "config" ) print "graph_title Forum Users\n"; print "graph_args -l 0\n"; print "graph_vlabel current users\n"; - print "graph_category Forum\n"; + print "graph_category forum\n"; print "graph_total Total\n"; print "members.label Members\n"; diff --git a/plugins/forums/vbulletin_users b/plugins/vbulletin/vbulletin_users similarity index 98% rename from plugins/forums/vbulletin_users rename to plugins/vbulletin/vbulletin_users index 9fb43980..663eb3d0 100755 --- a/plugins/forums/vbulletin_users +++ b/plugins/vbulletin/vbulletin_users @@ -60,7 +60,7 @@ if ( defined $ARGV[0] and $ARGV[0] eq "config" ) print "graph_title Forum Users\n"; print "graph_args -l 0\n"; print "graph_vlabel current users\n"; - print "graph_category Forum\n"; + print "graph_category forum\n"; print "graph_total Total\n"; print "members.label Members\n"; diff --git a/plugins/vdr/vdr_ b/plugins/vdr/vdr_ index ea10e593..f759a620 100755 --- a/plugins/vdr/vdr_ +++ b/plugins/vdr/vdr_ @@ -2,7 +2,7 @@ # Version 1.2 # - kein div 0 Fehler mehr wenn der Host nicht zu erreichen ist -# - Serien Timer werden nun seperat gezaehlt (anzahl pro Woche) +# - Serien Timer werden nun separat gezaehlt (anzahl pro Woche) # - im Namen kann munic conform der hostname mit angegeben werden: vdr_localhost vdr_192.168.0.2, ... (localhost ist default) # - Timer werden nur ignoriert wenn sie 0(inaktiv) als Status haben # - Erkennung der neuen Aufzeichnungen passte nicht mehr @@ -33,7 +33,7 @@ if (($ARGV[0])&&($ARGV[0] eq "config")){ # print "graph_args --upper-limit 50 -l 0\n"; print "graph_args -l 0\n"; print "graph_vlabel Anzahl\n"; - print "graph_category VDR\n"; + print "graph_category tv\n"; print "graph_info Diese Graphen zeigen verschiedene Werte des VDR.\n"; print "timer.label Timer (Anzahl)\n"; @@ -134,11 +134,11 @@ sub ermittelnTimer(){ } if (length($anzahl) && $anzahl =~ /\D/){ - #print "Serie"; + #print "Series"; $anzahlSerienTimer+=length($anzahl); $anzahlSerienMinuten+=$dauer*length($anzahl); } else { - #print "keine Serie"; + #print "keine Series"; $anzahlTimer++; $anzahlMinuten+=$dauer; } diff --git a/plugins/virtualization/virtualbox_cpu_kernel b/plugins/virtualbox/virtualbox_cpu_kernel old mode 100644 new mode 100755 similarity index 100% rename from plugins/virtualization/virtualbox_cpu_kernel rename to plugins/virtualbox/virtualbox_cpu_kernel diff --git a/plugins/virtualization/virtualbox_cpu_user b/plugins/virtualbox/virtualbox_cpu_user old mode 100644 new mode 100755 similarity index 100% rename from plugins/virtualization/virtualbox_cpu_user rename to plugins/virtualbox/virtualbox_cpu_user diff --git a/plugins/virtualization/virtualbox_ram_usage b/plugins/virtualbox/virtualbox_ram_usage old mode 100644 new mode 100755 similarity index 100% rename from plugins/virtualization/virtualbox_ram_usage rename to plugins/virtualbox/virtualbox_ram_usage diff --git a/plugins/virtualization/vmware/esx_ b/plugins/vmware/esx_ similarity index 99% rename from plugins/virtualization/vmware/esx_ rename to plugins/vmware/esx_ index 42c32759..4c4ad4b4 100755 --- a/plugins/virtualization/vmware/esx_ +++ b/plugins/vmware/esx_ @@ -710,3 +710,7 @@ sub munin_print { } } } + +# for Munin Plugin Gallery +# graph_category virtualization + diff --git a/plugins/virtualization/vmware/esxcli_env_ b/plugins/vmware/esxcli_env_ similarity index 100% rename from plugins/virtualization/vmware/esxcli_env_ rename to plugins/vmware/esxcli_env_ diff --git a/plugins/virtualization/vmware/esxi b/plugins/vmware/esxi similarity index 98% rename from plugins/virtualization/vmware/esxi rename to plugins/vmware/esxi index 54de7b8f..7aef50a3 100755 --- a/plugins/virtualization/vmware/esxi +++ b/plugins/vmware/esxi @@ -139,7 +139,7 @@ if [ "$1" = "config" ]; then echo 'graph_args -l 0' echo 'graph_scale no' echo 'graph_vlabel CPU usage in MHz' - echo 'graph_category Virtualization' + echo 'graph_category virtualization' echo 'graph_info This graph shows the average CPU MHz used by each domain' name="Domain_0" @@ -176,7 +176,7 @@ EOF echo 'graph_args -l 0' echo 'graph_scale no' echo 'graph_vlabel Memory usage in Megabytes' - echo 'graph_category Virtualization' + echo 'graph_category virtualization' echo 'graph_info This graph shows the average Memory used by each domain' name="Domain_0" diff --git a/plugins/sensors/esxi__sensors b/plugins/vmware/esxi__sensors similarity index 100% rename from plugins/sensors/esxi__sensors rename to plugins/vmware/esxi__sensors diff --git a/plugins/virtualization/vmware/fusion_ b/plugins/vmware/fusion_ similarity index 98% rename from plugins/virtualization/vmware/fusion_ rename to plugins/vmware/fusion_ index ec71d8d6..6ae2eec6 100755 --- a/plugins/virtualization/vmware/fusion_ +++ b/plugins/vmware/fusion_ @@ -62,7 +62,7 @@ my @lines=split(/\n/,$output); if ( exists $ARGV[0] and $ARGV[0] eq "config" ) { my $lcount = 0; - my $base_config = "graph_category Virtualization\n"; + my $base_config = "graph_category virtualization\n"; if( $type eq "pcpu" ) { print $base_config; diff --git a/plugins/virtualization/vmware/vm_cpu_load b/plugins/vmware/vm_cpu_load similarity index 94% rename from plugins/virtualization/vmware/vm_cpu_load rename to plugins/vmware/vm_cpu_load index d8ecbaae..00829d49 100755 --- a/plugins/virtualization/vmware/vm_cpu_load +++ b/plugins/vmware/vm_cpu_load @@ -16,7 +16,7 @@ if [ "$1" = "config" ]; then echo 'graph_title VMware CPU-Load' echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel Load of VMware VMs' - echo 'graph_category Virtualization' + echo 'graph_category cpu' while [ $i -lt ${#VMX[*]} ] do diff --git a/plugins/voip/murmur_users b/plugins/voip/murmur_users index 22ad34ef..9da3df2a 100755 --- a/plugins/voip/murmur_users +++ b/plugins/voip/murmur_users @@ -19,7 +19,7 @@ Requirements: Notice: - script allows the usage of the 'config' and 'autoconf' parameters during startup, make fure you edt config section before running it - $limit - number of lines to tail from the lgo file, better keep it below 5000 for lower cpu load, - aditionally on busy servers you can keep it really low, suggested 3x maximum number of users online + additionally on busy servers you can keep it really low, suggested 3x maximum number of users online - tested on PHP 5.2.6-3 with Suhosin-Patch 0.9.6.2 (cli) (built: Aug 21 2008 17:02:32) murmur 1.1.4 precompiled binaries from sourceforge net, all running under debian etch diff --git a/plugins/voip/murmurice_host_port_id_description_avg b/plugins/voip/murmurice_host_port_id_description_avg index 37f690dc..600232c0 100755 --- a/plugins/voip/murmurice_host_port_id_description_avg +++ b/plugins/voip/murmurice_host_port_id_description_avg @@ -21,7 +21,7 @@ Requirements: Configuration: 1. link creation -create a symlink to thie file in the following format: +create a symlink to the file in the following format: murmurice_ip_port_sid_description_avg where ip/port correspond to the ip and port on which the mumble ice interface listens to sid is the server id we want to query (mumble can run multiple servers ) diff --git a/plugins/voldemort/voldemort b/plugins/voldemort/voldemort old mode 100644 new mode 100755 index d10873b8..c11f9469 --- a/plugins/voldemort/voldemort +++ b/plugins/voldemort/voldemort @@ -28,7 +28,7 @@ if ARGV[0] == "config" puts "multigraph voldemort_#{key.gsub(" ", "_")}" puts "graph_title #{key}" puts "graph_scale no" - puts "graph_category voldemort" + puts "graph_category search" puts "graph_vlabel #{keys[key]['vlabel']}" for data in keys[key]['values'] do puts "#{data}.type #{keys[key]['type']}" diff --git a/plugins/openvpn/openvpn_as_mtime b/plugins/vpn/openvpn_as_mtime similarity index 98% rename from plugins/openvpn/openvpn_as_mtime rename to plugins/vpn/openvpn_as_mtime index c260a598..7dbf90ac 100755 --- a/plugins/openvpn/openvpn_as_mtime +++ b/plugins/vpn/openvpn_as_mtime @@ -56,7 +56,7 @@ if [ "$1" = "config" ]; then echo 'graph_args --base 1000 -l 0 ' echo 'graph_vlabel Time in minutes' echo 'graph_scale no' - echo 'graph_category OpenVPN' + echo 'graph_category network' echo 'graph_info Indicate the medium time of the logged users.' echo "time.label Users" diff --git a/plugins/openvpn/openvpn_as_traffic b/plugins/vpn/openvpn_as_traffic similarity index 97% rename from plugins/openvpn/openvpn_as_traffic rename to plugins/vpn/openvpn_as_traffic index 61e4de2f..d59e7fee 100755 --- a/plugins/openvpn/openvpn_as_traffic +++ b/plugins/vpn/openvpn_as_traffic @@ -49,7 +49,7 @@ if [ "$1" = "config" ]; then echo "graph_title OpenVPN traffic" echo 'graph_args --base 1000' echo 'graph_vlabel Bytes in (-) / out (+)' - echo 'graph_category OpenVPN' + echo 'graph_category network' echo "graph_info OpenVPN traffic" diff --git a/plugins/openvpn/openvpn_as_ttime b/plugins/vpn/openvpn_as_ttime similarity index 98% rename from plugins/openvpn/openvpn_as_ttime rename to plugins/vpn/openvpn_as_ttime index dfbd1757..06809a92 100755 --- a/plugins/openvpn/openvpn_as_ttime +++ b/plugins/vpn/openvpn_as_ttime @@ -57,7 +57,7 @@ if [ "$1" = "config" ]; then echo 'graph_args --base 1000 -l 0 ' echo 'graph_vlabel Time in minutes' echo 'graph_scale no' - echo 'graph_category OpenVPN' + echo 'graph_category network' echo 'graph_info Indicate the user logged time.' # If none, print 0 diff --git a/plugins/openvpn/openvpn_as_users b/plugins/vpn/openvpn_as_users similarity index 97% rename from plugins/openvpn/openvpn_as_users rename to plugins/vpn/openvpn_as_users index 640a48b6..3d655cb6 100755 --- a/plugins/openvpn/openvpn_as_users +++ b/plugins/vpn/openvpn_as_users @@ -52,7 +52,7 @@ if [ "$1" = "config" ]; then echo 'graph_args --base 1000 -l 0 ' echo 'graph_vlabel Users' echo 'graph_scale no' - echo 'graph_category OpenVPN' + echo 'graph_category network' echo 'graph_info Indicate the number of vpn users and the license limit.' echo "users.label Users" diff --git a/plugins/openvpn/openvpn_multi b/plugins/vpn/openvpn_multi similarity index 96% rename from plugins/openvpn/openvpn_multi rename to plugins/vpn/openvpn_multi index b42503ed..bafcc230 100755 --- a/plugins/openvpn/openvpn_multi +++ b/plugins/vpn/openvpn_multi @@ -38,7 +38,7 @@ sub config { print "graph_title OpenVPN traffic\n"; print "graph_args --base 1024 --lower-limit 0\n"; print "graph_vlabel Bytes Out (-) / In (+) per \${graph_period}\n"; - print "graph_category openvpn\n"; + print "graph_category network\n"; print "in.label recv\n"; print "in.type DERIVE\n"; print "in.min 0\n"; @@ -51,7 +51,7 @@ sub config { print "out.cdef out,1,*\n"; while () { - next if ($_ =~ /CLIENT LIST/ || $_ =~ /Updated/ || $_ =~ /Common Name/); + next if ($_ =~ /CLIENT LIST/ || $_ =~ /Updated/ || $_ =~ /Common Name/ || $_ =~ /^UNDEF,/); last if ($_ =~ /ROUTING TABLE/); # client,IP:port,in,out,D M N hour Y @@ -63,7 +63,7 @@ sub config { print "graph_title OpenVPN traffic for $name\n"; print "graph_args --base 1024 --lower-limit 0\n"; print "graph_vlabel Bytes Out (-) / In (+) per \${graph_period}\n"; - print "graph_category openvpn\n"; + print "graph_category network\n"; print "in.label recv\n"; print "in.type DERIVE\n"; print "in.min 0\n"; @@ -101,7 +101,7 @@ sub report { open FILE, $statusfile or die $!; while () { - next if ($_ =~ /CLIENT LIST/ || $_ =~ /Updated/ || $_ =~ /Common Name/); + next if ($_ =~ /CLIENT LIST/ || $_ =~ /Updated/ || $_ =~ /Common Name/ || $_ =~ /^UNDEF,/); last if ($_ =~ /ROUTING TABLE/); # client,IP:port,in,out,D M N hour Y diff --git a/plugins/virtualization/vserver/vserver_jiffies b/plugins/vserver/vserver_jiffies similarity index 50% rename from plugins/virtualization/vserver/vserver_jiffies rename to plugins/vserver/vserver_jiffies index 2e139a63..dfb8dd41 100755 --- a/plugins/virtualization/vserver/vserver_jiffies +++ b/plugins/vserver/vserver_jiffies @@ -1,7 +1,19 @@ -#!/bin/bash +#!/bin/zsh # # Created by Jan Rękorajski based on vserver_cpu_ plugin. # +# Obtained from https://github.com/munin-monitoring/contrib.git +# +# Changes by Andras Korn, 2015: +# +# * Convert to zsh +# * Fix to remove dots from vserver names (replace them with _) +# * Drop support for old (pre 2.6.19) kernels +# * Replace cat | grep | cut pipelines with a single sed +# * Add env.stripdomain (a domain name to strip from the +# end of vserver names when generating labels; be sure +# to include the leading dot) +# # Graph Vserver cumulative cpu usage stats # # Configuration variables @@ -11,47 +23,44 @@ # # see vserver_resources for example uses of configuration files -VSERVERS="$vservers" +VSERVERS=(${=vservers}) +STRIPDOMAIN="$stripdomain" -INFO=(`sed 's/.*:\t//' /proc/virtual/info 2>/dev/null || echo ''`) -KCIN="$[ 16#${INFO[2]} ]"; +INFO=($(sed 's/.*:\t//' /proc/virtual/info 2>/dev/null || echo '')) -# If this is 1, then VCI_SPACES is present in the kernel (new in 2.6.19) -if [ $[ (KCIN >> 10) & 1 ] -eq 1 ] -then - NAMELOC="nsproxy" -else - NAMELOC="cvirt" -fi +NAMELOC="nsproxy" -if [ -z "$VSERVERS" ] ; then - XIDS=`find /proc/virtual/* -type d -exec basename {} \;` +if [[ -z "$VSERVERS" ]] ; then + cd /proc/virtual + XIDS=($(echo *(/))) else # it's really more performant to specify vservers by ids or by linking but not in the configuration-file by name XIDS="" - for i in $VSERVERS ; do - if [ -d /proc/virtual/$i ] ; then - XIDS="${XIDS}${i} " + for i in $VSERVERS[@] ; do + if [[ -d /proc/virtual/$i ]] ; then + XIDS=($XIDS[@] $i) else - for j in `find /proc/virtual/* -type d -exec basename {} \;` ; do - if [ "$i" = "`cat /proc/virtual/$j/$NAMELOC |grep NodeName |cut -f2`" ] ; then - XIDS="${XIDS}${j} " + for j in /proc/virtual/*(/) -type d; do + if [[ "$i" = $(sed -n '/NodeName/s/^NodeName:[[:space:]]*//p' $j/$NAMELOC) ]]; then + XIDS=($XIDS[@] ${j:t}) fi done fi done fi -if [ "$1" = "config" ]; then - echo 'graph_category Virtualization' +if [[ "$1" = "config" ]]; then + echo 'graph_category virtualization' echo 'graph_args --base 1000' echo 'graph_title Vserver cpu usage' echo 'graph_vlabel jiffies used per ${graph_period}' echo 'graph_info Shows jiffies used on each vserver.' - for i in $XIDS ; do - LABEL=`grep NodeName /proc/virtual/$i/$NAMELOC | cut -f2` - NAME=`echo $LABEL | tr '-' '_'` + for i in $XIDS[@]; do + LABEL=$(sed -n '/NodeName/s/^NodeName:[[:space:]]*//p' /proc/virtual/$i/$NAMELOC) + LABEL=${LABEL%$STRIPDOMAIN} + NAME=${LABEL//./_} + NAME=${NAME//-/_} echo "${NAME}_hold.label on hold for cpu on $LABEL" echo "${NAME}_hold.info on hold for cpu on $LABEL." echo "${NAME}_hold.type COUNTER" @@ -65,8 +74,11 @@ if [ "$1" = "config" ]; then exit 0 fi -for i in $XIDS ; do - NAME=`grep NodeName /proc/virtual/$i/$NAMELOC | cut -f2 | tr '-' '_'` +for i in $XIDS[@]; do + LABEL=$(sed -n '/NodeName/s/^NodeName:[[:space:]]*//p' /proc/virtual/$i/$NAMELOC) + LABEL=${LABEL%$STRIPDOMAIN} + NAME=${LABEL//./_} + NAME=${NAME//-/_} awk -v name=$NAME -v u=0 -v s=0 -v h=0 ' /^cpu [0-9]+:/ { u+=$3; s+=$4; h+=$5} END { diff --git a/plugins/virtualization/vserver/vserver_limit_hits b/plugins/vserver/vserver_limit_hits similarity index 99% rename from plugins/virtualization/vserver/vserver_limit_hits rename to plugins/vserver/vserver_limit_hits index 8fcc7b7d..d4a690cc 100755 --- a/plugins/virtualization/vserver/vserver_limit_hits +++ b/plugins/vserver/vserver_limit_hits @@ -89,7 +89,7 @@ if [ "$1" = "config" ]; then echo "graph_title Vserver $resource limit hits" # echo 'graph_args --base 1024k -l 0' echo "graph_vlabel $resource limit hits" - echo 'graph_category Virtualization' + echo 'graph_category virtualization' echo "graph_info Shows number of hits on $resource limits by each vserver.'" for vserver_xid in $vservers ; do diff --git a/plugins/virtualization/vserver/vserver_limits b/plugins/vserver/vserver_limits similarity index 98% rename from plugins/virtualization/vserver/vserver_limits rename to plugins/vserver/vserver_limits index 3e6ab1b2..ea4317c5 100755 --- a/plugins/virtualization/vserver/vserver_limits +++ b/plugins/vserver/vserver_limits @@ -66,7 +66,7 @@ if [ "$1" = "config" ]; then echo "graph_title Vserver $resource limits" # echo 'graph_args --base 1024k -l 0' echo "graph_vlabel $resource limits" - echo 'graph_category Virtualization' + echo 'graph_category virtualization' echo "graph_info Shows current $resource limits for each vserver.'" for vserver_xid in $vservers ; do diff --git a/plugins/processes/vserver_procs b/plugins/vserver/vserver_procs similarity index 100% rename from plugins/processes/vserver_procs rename to plugins/vserver/vserver_procs diff --git a/plugins/weather/buienradar_ b/plugins/weather/buienradar_ index 9d740b9d..663b3cd0 100755 --- a/plugins/weather/buienradar_ +++ b/plugins/weather/buienradar_ @@ -108,7 +108,7 @@ if($stationName eq "") { # Output for config if(defined $ARGV[0] && $ARGV[0] eq 'config') { print "graph_title ".$stationName."\n"; - print "graph_category Weather\n"; + print "graph_category sensors\n"; if($LAN eq "nl") { # Dutch Language if($TMP eq "yes") { print "tmp.label Temperatuur (C)\n"; } @@ -181,4 +181,4 @@ sub print_stations { ." [6319] Terneuzen (Station Westdorpe) [6248] Hoorn (Station Wijdenes)\n" ." [6257] Wijk aan Zee (Station Wijk aan Zee) [6340] Woensdrecht (Station Woensdrecht)\n" ." [6239] Noordzee (Station Zeeplatform F-3) [6252] Noordzee (Station Zeeplatform K13)\n"; -} \ No newline at end of file +} diff --git a/plugins/sensors/humidity-sensor b/plugins/weather/humidity-sensor similarity index 100% rename from plugins/sensors/humidity-sensor rename to plugins/weather/humidity-sensor diff --git a/plugins/noaaport/novra_s300 b/plugins/weather/novra_s300 similarity index 98% rename from plugins/noaaport/novra_s300 rename to plugins/weather/novra_s300 index 3d42f55d..ca20cbc7 100755 --- a/plugins/noaaport/novra_s300 +++ b/plugins/weather/novra_s300 @@ -43,7 +43,7 @@ my $command = "$CMCS -ip $IP -pw $PW -xmlstatus"; if (defined($ARGV[0]) and ($ARGV[0] eq 'config')) { print "graph_title Novra S300\n"; print "graph_vlabel Signal\n"; - print "graph_category weather\n"; + print "graph_category sensors\n"; print "s300.signal Signal\n"; print "s300.ctn CtN\n"; exit(0); diff --git a/plugins/weather/openweather_ b/plugins/weather/openweather_ old mode 100644 new mode 100755 index bbd3c7a4..ffec4ea9 --- a/plugins/weather/openweather_ +++ b/plugins/weather/openweather_ @@ -18,103 +18,104 @@ # [openweather_*] # env.apikey XYZ -query_string=$(printf '%s' "${0#*_}" | tr '_' '=') -TMPFILE=$(mktemp) -trap 'rm -f $TMPFILE' EXIT +location=$(printf '%s' "${0#*_}" | tr '_' '=') +query_string="${location}&appid=${apikey}" +plugin_name=$( basename $0 ) -# API returns temp in K, we have to convert it in C +OWAPI=$( curl -s "http://api.openweathermap.org/data/2.5/weather?mode=xml&${query_string}") KELVIN_BIAS=273 -curl -s "http://api.openweathermap.org/data/2.5/weather?mode=xml&${query_string}&APPID=${apikey}" > $TMPFILE - -CITY=$(fgrep "new(timeout => 30); $ua->agent('Munin'); diff --git a/plugins/weather/temperatures b/plugins/weather/temperatures index 18808faa..52711c8b 100755 --- a/plugins/weather/temperatures +++ b/plugins/weather/temperatures @@ -2,7 +2,7 @@ # # Copyright (C) 2006 Lars Strand # -# Plugin to fetch temperature from weather.noaa.gov +# Plugin to fetch temperature from tgftp.nws.noaa.gov # # Parameters supported: # @@ -15,7 +15,7 @@ use strict; -# Find areacodes here http://weather.noaa.gov/ +# Find areacodes here http://tgftp.nws.noaa.gov/ my @wcode = undef; if (defined($ENV{wcode})) { @@ -43,7 +43,7 @@ if (defined $ARGV[0] and $ARGV[0] eq "autoconf") { } } -my $datasource = "http://weather.noaa.gov/pub/data/observations/metar/decoded/"; +my $datasource = "http://tgftp.nws.noaa.gov/data/observations/metar/decoded/"; my $ua = LWP::UserAgent->new(timeout => 30); $ua->agent('Munin'); diff --git a/plugins/weather/weather_ b/plugins/weather/weather_ index d91580b7..c083df41 100755 --- a/plugins/weather/weather_ +++ b/plugins/weather/weather_ @@ -11,51 +11,51 @@ re_tmp = re.compile('realTemp: "(\d+)"') re_hum = re.compile('relativeHumidity: "(\d+)"') re_loc = re.compile('locName: "([\w ]+)"') -#code = sys.argv[0][(sys.argv[0].rfind('_') + 1):] +# code = sys.argv[0][(sys.argv[0].rfind('_') + 1):] code = os.environ.get('code', sys.argv[0][(sys.argv[0].rfind('_') + 1):]) -if code == None: sys.exit(1) - -if len(sys.argv) == 2 and sys.argv[1] == "autoconf": - print "yes" +if not code: + sys.exit(1) +elif len(sys.argv) == 2 and sys.argv[1] == "autoconf": + print("yes") elif len(sys.argv) == 2 and sys.argv[1] == "config": - u = urllib.urlopen(url % code) - txt = u.read() - u.close() + u = urllib.urlopen(url % code) + txt = u.read() + u.close() - LOC_list = re_loc.findall(txt) - - if len(LOC_list): - LOC = LOC_list[0] - else: - LOC = "Unknown" + LOC_list = re_loc.findall(txt) - print 'graph_title Weather in %s' % LOC - print 'graph_vlabel Temperature and Humidity' - print 'graph_category sensors' + if len(LOC_list): + LOC = LOC_list[0] + else: + LOC = "Unknown" - print 'temperature.label Temperature' - print 'humidity.label Humidity' + print('graph_title Weather in %s' % LOC) + print('graph_vlabel Temperature and Humidity') + print('graph_category sensors') - print 'graph_args --base 1000 -l 0' + print('temperature.label Temperature') + print('humidity.label Humidity') + + print('graph_args --base 1000 -l 0') else: - u = urllib.urlopen(url % code) - txt = u.read() - u.close() + u = urllib.urlopen(url % code) + txt = u.read() + u.close() - TMP_F_list = re_tmp.findall(txt) - HUM_list = re_hum.findall(txt) + TMP_F_list = re_tmp.findall(txt) + HUM_list = re_hum.findall(txt) - if len(HUM_list): - HUM = HUM_list[0] - else: - sys.exit(1) + if len(HUM_list): + HUM = HUM_list[0] + else: + sys.exit(1) - if len(TMP_F_list): - TMP_F = TMP_F_list[0] - TMP_C = (int(TMP_F) - 32) * 5/9 - else: - sys.exit(1) + if len(TMP_F_list): + TMP_F = TMP_F_list[0] + TMP_C = (int(TMP_F) - 32) * 5/9 + else: + sys.exit(1) - print 'temperature.value %s' % TMP_C - print 'humidity.value %s' % HUM + print('temperature.value %s' % TMP_C) + print('humidity.value %s' % HUM) diff --git a/plugins/weather/weather_press_ b/plugins/weather/weather_press_ index 28c28ba3..ce55e3a2 100755 --- a/plugins/weather/weather_press_ +++ b/plugins/weather/weather_press_ @@ -1,11 +1,11 @@ #!/usr/bin/env python """ -munin US NOAA weather plugin (http://weather.noaa.gov) +munin US NOAA weather plugin (http://tgftp.nws.noaa.gov) Draws pressure in hPa. Copy/link file as 'weather_pressure_CODE', like: weather_pressure_LOWW for Austria, Vienna. -Get the code by going to http://weather.noaa.gov, selecting your +Get the code by going to http://tgftp.nws.noaa.gov, selecting your location, and copying the code from the address bar of your browser; should be something like CODE.html. @@ -16,35 +16,29 @@ import sys import urllib import re -url = 'http://weather.noaa.gov/pub/data/observations/metar/decoded/%s.TXT' +url = 'http://tgftp.nws.noaa.gov/data/observations/metar/decoded/%s.TXT' -re_hPa = re.compile('Pressure.*\((\d+) hPa\)') +re_hpa = re.compile('Pressure.*\((\d+) hPa\)') code = sys.argv[0][(sys.argv[0].rfind('_')+1):] -if code == None: sys.exit(1) - -if len(sys.argv) == 2 and sys.argv[1] == "autoconf": - - print "yes" - +if not code: + sys.exit(1) +elif len(sys.argv) == 2 and sys.argv[1] == "autoconf": + print("yes") elif len(sys.argv) == 2 and sys.argv[1] == "config": + print('graph_title Atmospheric pressure at code %s' % code) + print('graph_vlabel Pressure in hPa') + print('graph_category sensors') - print 'graph_title Atmospheric pressure at code %s' % code - print 'graph_vlabel Pressure in hPa' - print 'graph_category Weather' - - print 'pressure.label Pressure' - print 'pressure.type GAUGE' - print 'graph_args --base 1000 -l 850 -u 1050 --rigid' - print 'graph_scale no' - + print('pressure.label Pressure') + print('pressure.type GAUGE') + print('graph_args --base 1000 -l 850 -u 1050 --rigid') + print('graph_scale no') else: + u = urllib.urlopen(url % code) + txt = u.read() + u.close() - u = urllib.urlopen(url % code) - txt = u.read() - u.close() - - hPa = re_hPa.findall(txt)[0] - - print 'pressure.value %s' % hPa + hpa = re_hpa.findall(txt)[0] + print('pressure.value %s' % hpa) diff --git a/plugins/weather/weather_temp_ b/plugins/weather/weather_temp_ index 5cd74b55..2addff40 100755 --- a/plugins/weather/weather_temp_ +++ b/plugins/weather/weather_temp_ @@ -1,11 +1,11 @@ #!/usr/bin/env python """ -munin US NOAA weather plugin (http://weather.noaa.gov) +munin US NOAA weather plugin (http://tgftp.nws.noaa.gov) Draws temperature/dew point in C. Copy/link file as 'weather_temp_CODE', like: weather_temp_LOWW for Austria, Vienna. -Get the code by going to http://weather.noaa.gov, selecting your +Get the code by going to http://tgftp.nws.noaa.gov, selecting your location, and copying the code from the address bar of your browser; should be something like CODE.html. @@ -16,38 +16,31 @@ import sys import urllib import re -url = 'http://weather.noaa.gov/pub/data/observations/metar/decoded/%s.TXT' +url = 'http://tgftp.nws.noaa.gov/data/observations/metar/decoded/%s.TXT' re_C = re.compile('Temperature:.*\((-?\d+\.?\d?) C\)') re_DewC = re.compile('Dew.*\((-?\d+\.?\d?) C\)') code = sys.argv[0][(sys.argv[0].rfind('_')+1):] -if code == None: sys.exit(1) -if len(sys.argv) == 2 and sys.argv[1] == "autoconf": - - print "yes" +if not code: + sys.exit(1) +elif len(sys.argv) == 2 and sys.argv[1] == "autoconf": + print("yes") elif len(sys.argv) == 2 and sys.argv[1] == "config": - - print 'graph_title Temperature and Dew Point at code %s' % code - print 'graph_vlabel Temperature and Dew Point in C' - print 'graph_category Weather' - - print 'temperature.label Temperature' - print 'dewpoint.label Dew Point' - - print 'graph_args --base 1000 -l 0' - + print('graph_title Temperature and Dew Point at code %s' % code) + print('graph_vlabel Temperature and Dew Point in C') + print('graph_category sensors') + print('temperature.label Temperature') + print('dewpoint.label Dew Point') + print('graph_args --base 1000 -l 0') else: + u = urllib.urlopen(url % code) + txt = u.read() + u.close() - u = urllib.urlopen(url % code) - txt = u.read() - u.close() - - C = re_C.findall(txt)[0] - DewC = re_DewC.findall(txt)[0] - - print 'temperature.value %s' % C - print 'dewpoint.value %s' % DewC - + C = re_C.findall(txt)[0] + DewC = re_DewC.findall(txt)[0] + print('temperature.value %s' % C) + print('dewpoint.value %s' % DewC) diff --git a/plugins/sensors/wfrog b/plugins/weather/wfrog similarity index 100% rename from plugins/sensors/wfrog rename to plugins/weather/wfrog diff --git a/plugins/websphere/webspherelogin b/plugins/websphere/webspherelogin index a51480fb..e1744c55 100755 --- a/plugins/websphere/webspherelogin +++ b/plugins/websphere/webspherelogin @@ -8,7 +8,7 @@ SITEANALYZERLOG="/opt/WebSphere/SiteAnalyzer/sa.log" if [ "$1" = "config" ]; then echo 'graph_title WebSphere Portal online users' - echo 'graph_category websphere' + echo 'graph_category appserver' # echo 'graph_order total actual' echo 'graph_order actual' echo 'graph_vlabel Users' diff --git a/plugins/wifi/ath9k_ b/plugins/wifi/ath9k_ new file mode 100755 index 00000000..a43d844d --- /dev/null +++ b/plugins/wifi/ath9k_ @@ -0,0 +1,610 @@ +#!/bin/sh +# weird shebang? See below: "interpreter selection" +# +# Collect information related to ath9k wireless events and states. +# * rate control statistics ("rc_stats") +# * events (dropped, transmitted, beacon loss, ...) +# * traffic (packets, bytes) +# * DFS events (processed patterns, approved signals) +# +# All data is collected for each separate station (in case of multiple +# connected peers). Combined graphs are provided as a summary. +# +# +# This plugin works with the following python interpreters: +# * Python 3 +# * micropython +# +# +# The following graphs are generated for each physical ath9k interface: +# phy0_wifi0_traffic +# phy0_wifi0_traffic.station0 +# ... +# pyh0_wifi0_events +# phy0_wifi0_events.station0 +# ... +# pyh0_wifi0_rc_stats +# phy0_wifi0_rc_stats.station0 +# ... +# +# +# Copyright (C) 2015-2018 Lars Kruse +# +# 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 . +# +# Magic markers +# #%# capabilities=autoconf suggest +# #%# family=auto + +"""true" +# ****************** Interpreter Selection *************** +# This unbelievable dirty hack allows to find a suitable python interpreter. +# This is specifically useful for OpenWRT where typically only micropython is available. +# +# This "execution hack" works as follows: +# * the script is executed by busybox ash or another shell +# * the above line (three quotes before and one quote after 'true') evaluates differently for +# shell and python: +# * shell: run "true" (i.e. nothing happens) +# * python: ignore everything up to the next three consecutive quotes +# Thus we may place shell code here that will take care for selecting an interpreter. + +# prefer micropython if it is available - otherwise fall back to python 3 +MICROPYTHON_BIN=$(which micropython || true) +if [ -n "$MICROPYTHON_BIN" ]; then + "$MICROPYTHON_BIN" "$0" "$@" +else + python3 "$0" "$@" +fi +exit $? + +# For shell: ignore everything starting from here until the last line of this file. +# This is necessary for syntax checkers that try to complain about invalid shell syntax below. +true < IP """ + arp_cache = {} + # example content: + # IP address HW type Flags HW address Mask Device + # 192.168.2.70 0x1 0x0 00:00:00:00:00:00 * eth0.10 + # 192.168.12.76 0x1 0x2 24:a4:3c:fd:76:98 * eth1.10 + for line in open("/proc/net/arp", "r").read().split("\n"): + # skip empty lines + if line: + tokens = line.split() + ip, mac = tokens[0], tokens[3] + # the header line can be ignored - all other should have well-formed MACs + if ":" in mac: + # ignore remote peers outside of the broadcast domain + if mac != "00:00:00:00:00:00": + arp_cache[mac] = ip + return arp_cache + + def _parse_stations(self): + stations_base = os.path.join(self._path, "stations") + arp_cache = self._parse_arp_cache() + for item in os.listdir(stations_base): + peer_mac = item + # use the IP or fall back to the MAC without separators (":") + if peer_mac in arp_cache: + label = arp_cache[peer_mac] + key = peer_mac.replace(":", "") + else: + label = peer_mac + key = "host_" + peer_mac.replace(":", "").replace(".", "") + yield Station(label, key, os.path.join(stations_base, item)) + + def get_config(self, scope): + yield from Station.get_summary_config(scope, self.stations, self._graph_base) + for station in self.stations: + yield from station.get_config(scope, self._graph_base) + yield "" + + def get_values(self, scope): + yield from Station.get_summary_values(scope, self.stations, self._graph_base) + for station in self.stations: + yield from station.get_values(scope, self._graph_base) + yield "" + + +class WifiPhy: + + def __init__(self, name, path, graph_base): + self._path = path + self._graph_base = graph_base + self.name = name + self.dfs_events = self._parse_dfs_events() + self.interfaces = tuple(self._parse_interfaces()) + + def _parse_dfs_events(self): + result = {} + fname = os.path.join(self._path, "ath9k", "dfs_stats") + if not os.path.exists(fname): + # older ath9k modules (e.g. Linux 3.3) did not provide this data + return {} + for line in open(fname, "r").read().split("\n"): + tokens = line.split(":") + if len(tokens) == 2: + label, value = tokens[0].strip(), tokens[1].strip() + if label in DFS_EVENT_COUNTERS: + fieldname = DFS_EVENT_COUNTERS[label] + result[fieldname] = value + return result + + def _parse_interfaces(self): + for item in os.listdir(self._path): + if item.startswith("netdev:"): + wifi = item.split(":", 1)[1] + label = "{phy}/{interface}".format(phy=self.name, interface=wifi) + wifi_path = os.path.join(self._path, item) + graph_base = "{base}_{phy}_{interface}".format(base=self._graph_base, + phy=self.name, interface=wifi) + yield WifiInterface(label, wifi_path, graph_base) + + def get_config(self, scope): + if scope == "dfs_events": + yield "multigraph {graph_base}_dfs_events".format(graph_base=self._graph_base) + yield "graph_title DFS Events" + yield "graph_vlabel events per second" + yield "graph_args --base 1000 --logarithmic" + yield "graph_category wireless" + for label, fieldname in DFS_EVENT_COUNTERS.items(): + yield "{fieldname}.label {label}".format(fieldname=fieldname, label=label) + yield "{fieldname}.type COUNTER".format(fieldname=fieldname) + yield "" + else: + for interface in self.interfaces: + yield from interface.get_config(scope) + + def get_values(self, scope): + if scope == "dfs_events": + yield "multigraph {graph_base}_dfs_events".format(graph_base=self._graph_base) + for fieldname, value in self.dfs_events.items(): + yield "{fieldname}.value {value}".format(fieldname=fieldname, value=value) + yield "" + else: + for interface in self.interfaces: + yield from interface.get_values(scope) + + +class Ath9kDriver: + + def __init__(self, path, graph_base): + self._path = path + self._graph_base = graph_base + self.phys = list(self._parse_phys()) + + def _parse_phys(self): + if not os.path.exists(self._path): + return + for phy in os.listdir(self._path): + phy_path = os.path.join(self._path, phy) + graph_base = "{base}_{phy}".format(base=self._graph_base, phy=phy) + yield WifiPhy(phy, phy_path, graph_base) + + def get_config(self, scope): + for phy in self.phys: + yield from phy.get_config(scope) + + def get_values(self, scope): + for phy in self.phys: + yield from phy.get_values(scope) + + def has_dfs_support(self): + for phy in self.phys: + if phy.dfs_events: + return True + return False + + def has_devices(self): + return len(self.phys) > 0 + + +def _get_up_down_pair(unit, key_up, key_down, factor=None, divider=None, use_negative=True): + """ return all required statements for a munin-specific up/down value pair + "factor" or "divider" can be given for unit conversions + """ + for key in (key_up, key_down): + if use_negative: + yield "{key}.label {unit}".format(key=key, unit=unit) + else: + yield "{key}.label {key} {unit}".format(key=key, unit=unit) + yield "{key}.type COUNTER".format(key=key) + if factor: + yield "{key}.cdef {key},{factor},*".format(key=key, factor=factor) + if divider: + yield "{key}.cdef {key},{divider},/".format(key=key, divider=divider) + if use_negative: + yield "{key_down}.graph no".format(key_down=key_down) + yield "{key_up}.negative {key_down}".format(key_up=key_up, key_down=key_down) + + +def get_scope(): + called_name = os.path.basename(sys.argv[0]) + name_prefix = "ath9k_" + if called_name.startswith(name_prefix): + scope = called_name[len(name_prefix):] + if scope not in PLUGIN_SCOPES: + print_error("Invalid scope requested: {0} (expected: {1})" + .format(scope, PLUGIN_SCOPES)) + sys.exit(2) + else: + print_error("Invalid filename - failed to discover plugin scope") + sys.exit(2) + return scope + + +def print_error(message): + # necessary fallback for micropython + linesep = getattr(os, "linesep", "\n") + sys.stderr.write(message + linesep) + + +def do_fetch(ath9k): + for item in ath9k.get_values(get_scope()): + print(item) + + +def do_config(ath9k): + for item in ath9k.get_config(get_scope()): + print(item) + + +if __name__ == "__main__": + ath9k = Ath9kDriver(SYS_BASE_DIR, GRAPH_BASE_NAME) + # parse arguments + if len(sys.argv) > 1: + if sys.argv[1] == "config": + do_config(ath9k) + if os.getenv("MUNIN_CAP_DIRTYCONFIG") == "1": + do_fetch(ath9k) + sys.exit(0) + elif sys.argv[1] == "autoconf": + if os.path.exists(SYS_BASE_DIR): + print('yes') + else: + print('no (missing ath9k driver sysfs directory: {})'.format(SYS_BASE_DIR)) + sys.exit(0) + elif sys.argv[1] == "suggest": + if ath9k.has_devices(): + for scope in PLUGIN_SCOPES: + # skip the "dfs_events" scope if there is not DFS support + if (scope != "dfs_events") or ath9k.has_dfs_support(): + print(scope) + sys.exit(0) + elif sys.argv[1] == "version": + print_error('olsrd Munin plugin, version %s' % plugin_version) + sys.exit(0) + elif sys.argv[1] == "": + # ignore + pass + else: + # unknown argument + print_error("Unknown argument") + sys.exit(1) + + do_fetch(ath9k) + +# final marker for shell / python hybrid script (see "Interpreter Selection") +EOF = True +EOF diff --git a/plugins/wifi/example-graphs/ath9k_-stats_phy0_dfs_events_year.png b/plugins/wifi/example-graphs/ath9k_-stats_phy0_dfs_events_year.png new file mode 100644 index 00000000..eb2fbe4f Binary files /dev/null and b/plugins/wifi/example-graphs/ath9k_-stats_phy0_dfs_events_year.png differ diff --git a/plugins/wifi/example-graphs/ath9k_-stats_phy0_phy0_wlan0_events_day.png b/plugins/wifi/example-graphs/ath9k_-stats_phy0_phy0_wlan0_events_day.png new file mode 100644 index 00000000..a32f0455 Binary files /dev/null and b/plugins/wifi/example-graphs/ath9k_-stats_phy0_phy0_wlan0_events_day.png differ diff --git a/plugins/wifi/example-graphs/ath9k_-stats_rcstats_day.png b/plugins/wifi/example-graphs/ath9k_-stats_rcstats_day.png new file mode 100644 index 00000000..a9f03400 Binary files /dev/null and b/plugins/wifi/example-graphs/ath9k_-stats_rcstats_day.png differ diff --git a/plugins/wifi/example-graphs/wireless_channel_active_-week.png b/plugins/wifi/example-graphs/wireless_channel_active_-week.png new file mode 100644 index 00000000..472fe858 Binary files /dev/null and b/plugins/wifi/example-graphs/wireless_channel_active_-week.png differ diff --git a/plugins/wifi/example-graphs/wireless_channel_active_-year.png b/plugins/wifi/example-graphs/wireless_channel_active_-year.png new file mode 100644 index 00000000..3f40a721 Binary files /dev/null and b/plugins/wifi/example-graphs/wireless_channel_active_-year.png differ diff --git a/plugins/wifi/example-graphs/wireless_channel_occupation_-day.png b/plugins/wifi/example-graphs/wireless_channel_occupation_-day.png new file mode 100644 index 00000000..3ec54227 Binary files /dev/null and b/plugins/wifi/example-graphs/wireless_channel_occupation_-day.png differ diff --git a/plugins/wifi/example-graphs/wireless_signal_noise_-day.png b/plugins/wifi/example-graphs/wireless_signal_noise_-day.png new file mode 100644 index 00000000..6c4c46b8 Binary files /dev/null and b/plugins/wifi/example-graphs/wireless_signal_noise_-day.png differ diff --git a/plugins/wifi/example-graphs/wireless_signal_ranges_-day.png b/plugins/wifi/example-graphs/wireless_signal_ranges_-day.png new file mode 100644 index 00000000..830c710f Binary files /dev/null and b/plugins/wifi/example-graphs/wireless_signal_ranges_-day.png differ diff --git a/plugins/network/wifi_signal b/plugins/wifi/wifi_signal similarity index 97% rename from plugins/network/wifi_signal rename to plugins/wifi/wifi_signal index 7892cbe5..4456532b 100755 --- a/plugins/network/wifi_signal +++ b/plugins/wifi/wifi_signal @@ -6,7 +6,7 @@ # License: GPL v. 2 # #%# family=auto -#%# capabilitoes=autoconf +#%# capabilities=autoconf PNWL=/proc/net/wireless diff --git a/plugins/wifi/wireless_channel_occupation_ b/plugins/wifi/wireless_channel_occupation_ new file mode 100755 index 00000000..20999b1c --- /dev/null +++ b/plugins/wifi/wireless_channel_occupation_ @@ -0,0 +1,143 @@ +#!/bin/sh + +: << =cut + +=head1 NAME + +wireless_channel_occupation_ - Monitor occupation of wireless channels + + +=head1 APPLICABLE SYSTEMS + +All systems with at least one wireless interface and the the tool "iw". + +The wifi channel occupation is parsed from the output of "iw dev wlan0 survey dump". + + +=head1 CONFIGURATION + +Symlink this plugin with the name of the wifi interface added (e.g. "wlan0"). + +Root permissions are probably required for accessing "iw". + + [wireless_channel_occupation_*] + user root + + +=head1 VERSION + + 1.1 + + +=head1 AUTHOR + +Lars Kruse + + +=head1 LICENSE + +GPLv3 or above + + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf suggest + +=cut + +set -eu + + +SCRIPT_PREFIX="wireless_channel_occupation_" + + +clean_fieldname() { + echo "$1" | sed 's/^\([^A-Za-z_]\)/_\1/; s/[^A-Za-z0-9_]/_/g' +} + + +get_wifi_interfaces() { + iw dev | grep Interface | awk '{print $2}' +} + +get_wifi_device_from_suffix() { + local suffix + local real_dev + # pick the part after the basename of the real file + suffix=$(basename "$0" | sed "s/^$SCRIPT_PREFIX//") + for real_dev in $(get_wifi_interfaces); do + [ "$suffix" != "$(clean_fieldname "$real_dev")" ] || echo "$real_dev" + done | head -1 +} + + +do_config() { + local device + local dev_field + device=$(get_wifi_device_from_suffix) + [ -z "$device" ] && echo >&2 "Invalid wifi device name given" && return 1 + echo "graph_title Channel Occupation of $device" + echo "graph_vlabel %" + echo "graph_category wireless" + echo "graph_args --base 1000 -r --lower-limit 0 --upper-limit 100" + dev_field=$(clean_fieldname "$device") + + # active: listening time on this channel (usually: 5 minutes = 300000ms) + echo "${dev_field}_active.label Transmit" + echo "${dev_field}_active.type DERIVE" + echo "${dev_field}_active.graph no" + + # busy = receive + transmit + unknown + echo "${dev_field}_busy.label unknown" + echo "${dev_field}_busy.type DERIVE" + echo "${dev_field}_busy.draw AREA" + echo "${dev_field}_busy.cdef 100,1,${dev_field}_active,${dev_field}_busy,${dev_field}_receive,${dev_field}_transmit,+,-,/,/,*" + + # receive: this radio receives traffic for itself + echo "${dev_field}_receive.label Receive" + echo "${dev_field}_receive.type DERIVE" + echo "${dev_field}_receive.draw STACK" + echo "${dev_field}_receive.cdef 100,${dev_field}_receive,${dev_field}_active,/,*" + + # transmit: this radio transmits traffic + echo "${dev_field}_transmit.label Transmit" + echo "${dev_field}_transmit.type DERIVE" + echo "${dev_field}_transmit.draw STACK" + echo "${dev_field}_transmit.cdef 100,${dev_field}_transmit,${dev_field}_active,/,*" +} + + +do_fetch() { + local device + device=$(get_wifi_device_from_suffix) + [ -z "$device" ] && echo >&2 "Invalid wifi device name given" && return 1 + iw dev "$device" survey dump \ + | grep -F -A 5 "[in use]" \ + | grep -E "channel (busy|receive|transmit|active) time:" \ + | awk '{print "'"${device}_"'"$2"'.value'",$4}' +} + + +if [ "${1:-}" = "autoconf" ]; then + if which iw >/dev/null; then + if [ -n "$(get_wifi_interfaces)" ]; then + echo "yes" + else + echo "no (missing wifi devices)" + fi + else + echo "no (missing 'iw' dependency)" + fi +elif [ "${1:-}" = "suggest" ]; then + for dev in $(get_wifi_interfaces); do + clean_fieldname "$dev" + done +elif [ "${1:-}" = "config" ]; then + do_config || exit 1 + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = "1" ]; then do_fetch; fi +else + do_fetch +fi + +exit 0 diff --git a/plugins/wifi/wireless_signal_noise_ b/plugins/wifi/wireless_signal_noise_ new file mode 100755 index 00000000..c8761519 --- /dev/null +++ b/plugins/wifi/wireless_signal_noise_ @@ -0,0 +1,172 @@ +#!/bin/sh + +: << =cut + +=head1 NAME + +wireless_signal_noise_ - Show signal strength and noise for all connected peers of wifi interface + +=head1 APPLICABLE SYSTEMS + +This plugin is suitable for wifi interfaces with a stable selection of peers (e.g. infrastructure). +It is probably not useful for hotspot-like scenarios. + +Information is parsed from the output of the tool "iwinfo" (OpenWrt) or "iw" (most systems, +incomplete information). + + +=head1 CONFIGURATION + +Symlink this plugin with the name of the wifi interface added (e.g. "wlan0"). + +Root permissions are probably required for accessing "iw". + + [wireless_signal_noise_*] + user root + + +=head1 VERSION + + 1.1 + + +=head1 AUTHOR + +Lars Kruse + + +=head1 LICENSE + +GPLv3 or above + + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf suggest + +=cut + + +set -eu + + +SCRIPT_PREFIX="wireless_signal_noise_" + + +# prefer "iwinfo" for information retrieval, if it is available +if which iwinfo >/dev/null; then + # "iwinfo" has a stable output format but is only available on openwrt + get_wifi_interfaces() { iwinfo | grep "^[a-zA-Z]" | awk '{print $1}'; } + # return MAC of peer and the signal strength + get_wifi_peers() { iwinfo "$1" assoclist | grep "^[0-9a-fA-F]" | awk '{print $1,$2}'; } + # the noise should be the same for all peers + get_wifi_noise() { iwinfo "$1" info | sed -n 's/^.* Noise: \([0-9-]\+\).*/\1/p'; } +else + # "iw" is available everywhere - but its output format is not recommended for non-humans + get_wifi_interfaces() { iw dev | awk '{ if ($1 == "Interface") print $2; }'; } + get_wifi_peers() { iw dev wlan0 station dump \ + | awk '{ if ($1 == "Station") mac=$2; if (($1 == "signal") && ($2 == "avg:")) print mac,$3}'; } + # TODO: there seems to be no way to retrieve the noise level via "iw" + get_wifi_noise() { echo; } +fi +if which arp >/dev/null; then + # openwrt does not provide 'arp' by default + get_arp() { arp -n; } +else + get_arp() { cat /proc/net/arp; } +fi + + +clean_fieldname() { + echo "$1" | sed 's/^\([^A-Za-z_]\)/_\1/; s/[^A-Za-z0-9_]/_/g' +} + + +get_ip_for_mac() { + local ip + ip=$(get_arp | grep -iw "$1$" | awk '{print $1}' | sort | head -1) + [ -n "$ip" ] && echo "$ip" && return 0 + # no IP found - return MAC instead + echo "$1" +} + + +get_wifi_device_from_suffix() { + local suffix + local real_dev + # pick the part after the basename of the real file + suffix=$(basename "$0" | sed "s/^$SCRIPT_PREFIX//") + for real_dev in $(get_wifi_interfaces); do + [ "$suffix" != "$(clean_fieldname "$real_dev")" ] || echo "$real_dev" + done | head -1 +} + + +do_config() { + local wifi + wifi=$(get_wifi_device_from_suffix) + [ -z "$wifi" ] && echo >&2 "Missing wifi: $wifi" && return 1 + echo "graph_title Wireless signal quality - $wifi" + echo "graph_args --upper-limit 0" + echo "graph_vlabel Signal and noise [dBm]" + echo "graph_category network" + echo "graph_info This graph shows the signal and noise for all wifi peers" + echo "noise.label Noise floor" + echo "noise.draw LINE" + # sub graphs for all peers + get_wifi_peers "$wifi" | while read -r mac signal; do + fieldname=$(clean_fieldname "peer_${mac}") + peer=$(get_ip_for_mac "$mac") + echo "signal_${fieldname}.label $peer" + echo "signal_${fieldname}.draw LINE" + done +} + + +do_fetch() { + local wifi + local peer_data + local noise + wifi=$(get_wifi_device_from_suffix) + [ -z "$wifi" ] && echo >&2 "Missing wifi: $wifi" && return 1 + peer_data=$(get_wifi_peers "$wifi") + echo "$peer_data" | while read -r mac signal; do + # ignore empty datasets + [ -z "$signal" ] && continue + fieldname=$(clean_fieldname "peer_${mac}") + echo "signal_${fieldname}.value $signal" + done + noise=$(get_wifi_noise "$wifi") + echo "noise.value ${noise:-U}" +} + + +ACTION="${1:-}" + +case "$ACTION" in + config) + do_config || exit 1 + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = "1" ]; then do_fetch; fi + ;; + autoconf) + if [ -z "$(get_wifi_interfaces)" ]; then + echo "no (no wifi interfaces found)" + else + echo "yes" + fi + ;; + suggest) + get_wifi_interfaces | while read -r ifname; do + clean_fieldname "$ifname" + done + ;; + "") + do_fetch + ;; + *) + echo >&2 "Invalid action (valid: config / suggest / autoconf / )" + echo >&2 + exit 2 + ;; +esac diff --git a/plugins/wifi/wireless_signal_ranges_ b/plugins/wifi/wireless_signal_ranges_ new file mode 100755 index 00000000..2f893970 --- /dev/null +++ b/plugins/wifi/wireless_signal_ranges_ @@ -0,0 +1,160 @@ +#!/bin/sh + +: << =cut + +=head1 NAME + +wireless_signal_ranges_ - Group and count all connected wifi peers by signal strength ranges + + +=head1 APPLICABLE SYSTEMS + +Information is parsed from the output of the tool "iwinfo" (OpenWrt) or "iw" (most systems). + +This plugin is suitable for wifi interfaces with a variable selection of peers (e.g. mobile +clients). + + +=head1 CONFIGURATION + +Symlink this plugin with the name of the wifi interface added (e.g. "wlan0"). + +Root permissions are probably required for accessing "iw". + + [wireless_signal_ranges_*] + user root + + +=head1 VERSION + + 1.1 + + +=head1 AUTHOR + +Lars Kruse + + +=head1 LICENSE + +GPLv3 or above + + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf suggest + +=cut + +set -eu + + +SCRIPT_PREFIX="wireless_signal_ranges_" + +# thresholds for signal quality ranges: ascending values +SIGNAL_THRESHOLDS="-88 -80 -60 0" + + +# prefer "iwinfo" for information retrieval, if it is available +if which iwinfo >/dev/null; then + # "iwinfo" has a stable output format but is only available on openwrt + get_wifi_interfaces() { iwinfo | grep "^[a-zA-Z]" | awk '{print $1}'; } + # return MAC of peer and the signal strength + get_wifi_peers() { iwinfo "$1" assoclist | grep "^[0-9a-fA-F]" | awk '{print $2}'; } +else + # "iw" is available everywhere - but its output format is not recommended for non-humans + get_wifi_interfaces() { iw dev | awk '{ if ($1 == "Interface") print $2; }'; } + get_wifi_peers() { iw dev wlan0 station dump \ + | awk '{ if (($1 == "signal") && ($2 == "avg:")) print $3}'; } +fi + + +clean_fieldname() { + echo "$1" | sed 's/^\([^A-Za-z_]\)/_\1/; s/[^A-Za-z0-9_]/_/g' +} + + +get_level_fieldname() { + echo "range_${1#-}" +} + + +get_wifi_device_from_suffix() { + local suffix + local real_dev + # pick the part after the basename of the real file + suffix=$(basename "$0" | sed "s/^$SCRIPT_PREFIX//") + for real_dev in $(get_wifi_interfaces); do + [ "$suffix" != "$(clean_fieldname "$real_dev")" ] || echo "$real_dev" + done | head -1 +} + + +do_config() { + local wifi + local lower + wifi=$(get_wifi_device_from_suffix) + [ -z "$wifi" ] && echo >&2 "Missing wifi: $wifi" && return 1 + echo "graph_title Wireless signal quality ranges - $wifi" + echo "graph_args --upper-limit 0" + echo "graph_vlabel Signal ranges" + echo "graph_category network" + echo "graph_info This graph shows numbers of peers with defined wifi signal ranges" + lower="noise" + for level in $SIGNAL_THRESHOLDS; do + fieldname=$(get_level_fieldname "$level") + echo "${fieldname}.label $lower...$level" + echo "${fieldname}.draw AREASTACK" + lower="$level" + done +} + + +do_fetch() { + local wifi + local peer_data + local previous_count + local current_count + local fieldname + wifi=$(get_wifi_device_from_suffix) + [ -z "$wifi" ] && echo >&2 "Missing wifi: $wifi" && return 1 + peer_data=$(get_wifi_peers "$wifi") + previous_count=0 + for level in $SIGNAL_THRESHOLDS; do + current_count=$(echo "$peer_data" | awk ' + BEGIN { count=0; } + { if (($1 != "") && ($1 <= '"$level"')) count++; } + END { print count; }') + fieldname=$(get_level_fieldname "$level") + echo "${fieldname}.value $((current_count - previous_count))" + previous_count="$current_count" + done +} + + +ACTION="${1:-}" + +case "$ACTION" in + config) + do_config || exit 1 + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = "1" ]; then do_fetch; fi + ;; + autoconf) + [ -z "$(get_wifi_interfaces)" ] && echo "no (no wifi interfaces found)" && exit 1 + echo "yes" + ;; + suggest) + get_wifi_interfaces | while read -r ifname; do + clean_fieldname "$ifname" + done + ;; + "") + do_fetch + ;; + *) + echo >&2 "Invalid action (valid: config / autoconf / suggest / )" + echo >&2 + exit 2 + ;; +esac diff --git a/plugins/other/wordpress b/plugins/wordpress/wordpress similarity index 98% rename from plugins/other/wordpress rename to plugins/wordpress/wordpress index 76cbafe8..f2f08a27 100755 --- a/plugins/other/wordpress +++ b/plugins/wordpress/wordpress @@ -56,6 +56,7 @@ Andre Darafarin, happypork.com if [ "$1" = "config" ]; then echo 'graph_title Wordpress average' + echo 'graph_category cms' echo 'graph_order posts comments pingbacks users' echo 'graph_vlabel Wordpress' echo 'graph_info Some Statistics of Wordpress' diff --git a/plugins/other/wordpress-mu-or-network b/plugins/wordpress/wordpress-mu-or-network similarity index 98% rename from plugins/other/wordpress-mu-or-network rename to plugins/wordpress/wordpress-mu-or-network index f62a75dd..a23074fe 100755 --- a/plugins/other/wordpress-mu-or-network +++ b/plugins/wordpress/wordpress-mu-or-network @@ -59,6 +59,7 @@ Andre Darafarin, happypork.com if [ "$1" = "config" ]; then echo 'graph_title Wordpress average' echo 'graph_order posts comments pingbacks users' + echo 'graph_category cms' echo 'graph_vlabel Wordpress' echo 'graph_info Some Statistics of Wordpress' echo 'posts.label Posts' diff --git a/plugins/wordpress/wordpress-multisite b/plugins/wordpress/wordpress-multisite new file mode 100755 index 00000000..17794010 --- /dev/null +++ b/plugins/wordpress/wordpress-multisite @@ -0,0 +1,122 @@ +#!/bin/sh +# wordpress-multisite plugin +: <<=cut +=head1 NAME + +Wordpress-Multisite Munin Plugin + +A Munin plugin to monitor posts, comments and pingbacks from every multisite instance. +It uses multigraphs and also shows the combined number of posts, comments, pingbacks, instances and users. + +Most database queries came from the wordpress-mu-plugin which was written by Andre Darafarin and Chris Bair + +=head1 CONFIGURATION + +The plugin need access to the wordpress database + +=head2 Config file + +Create the config file plugin-conf.d/wordpress with the following values: + + [wordpress*] + env.mysqlopts # I.e. -uroot -prootpass + env.mysqlconnection # Defaults to -hlocalhost + env.database # I.e. wordpress + env.dbprefix # Defaults to wp_ + env.networksize # Blogs are ordered by id in multigraph view. This value should contain the numbers + # of digits that are needed to fit all the blog id's in. This value influences the + # designation of data to munin and it will start collecting fresh data if you change + # this number. Defaults to 2, (so networks with <= 99 blogs will be sorted correctly) + +=head1 VERSION + +Version 0.4 (2017-01-13) + +=head1 AUTHOR + +Jonas Palm + +=head1 LICENSE + +GPLv3 or higher +=cut + +# fill vars +DB_OPTIONS=${mysqlopts} +DB_CONNECTION=${mysqlconnection:--hlocalhost} +DB_NAME=${database} +DB_PREFIX=${dbprefix:-wp_} +NETWORK_SIZE=${networksize:-2} + +MYSQL_CMD=$(which mysql) + +# wp_get dataname [blogid] +wp_get() { + local DB_QUERY= + local BLOGID= + if [ -n "$2" ] && [ "$2" -gt "1" ]; then + # DB prefix for every wordpress instance in the network + # Nr 1 is the main network blog and doesn't has a prefix + BLOGID=${2}_ + fi + + case $1 in + comments) DB_QUERY="SELECT COUNT(*) FROM ${DB_PREFIX}${BLOGID}comments WHERE comment_approved = '1' AND comment_type = '';" ;; + ids) DB_QUERY="SELECT blog_id FROM ${DB_PREFIX}blogs;" ;; + pingbacks) DB_QUERY="SELECT COUNT(*) FROM ${DB_PREFIX}${BLOGID}comments WHERE comment_approved = '1' AND comment_type = 'pingback';" ;; + posts) DB_QUERY="SELECT COUNT(*) FROM ${DB_PREFIX}${BLOGID}posts WHERE post_status = 'publish' AND post_password = '' AND post_type = 'post';" ;; + title) DB_QUERY="SELECT option_value FROM ${DB_PREFIX}${BLOGID}options WHERE option_name = 'siteurl';" ;; + users) DB_QUERY="SELECT COUNT(*) FROM ${DB_PREFIX}users;" + esac + + "$MYSQL_CMD" $DB_CONNECTION $DB_OPTIONS "$DB_NAME" --column-names=0 -s --execute="$DB_QUERY" +} + +# whole network +if [ "$1" = "config" ]; then + echo "multigraph wordpress" + echo "graph_title Wordpress Mulitsite" + echo "graph_order instances users posts comments pingbacks" + echo "graph_vlabel Wordpress" + echo "graph_category cms" + echo "graph_info Some Statistics of Wordpress" + echo "instances.label Instances" + echo "users.label Users" + echo "posts.label Posts" + echo "comments.label Comments" + echo "pingbacks.label Pingbacks" +else + for n in $(wp_get ids); do + POSTS=$((POSTS + $(wp_get posts "$n"))) + COMMENTS=$((COMMENTS + $(wp_get comments "$n"))) + PINGBACKS=$((PINGBACKS + $(wp_get pingbacks "$n"))) + CNT=$((CNT + 1)) + done + + echo "multigraph wordpress" + echo "posts.value $POSTS" + echo "comments.value $COMMENTS" + echo "pingbacks.value $PINGBACKS" + echo "instances.value $CNT" + echo "users.value $(wp_get users)" +fi + +# single blogs +for n in $(wp_get ids); do + blogid_sortable="$(printf "%0${NETWORK_SIZE}d" "$n")" + + if [ "$1" = "config" ]; then + echo "multigraph wordpress.site_${blogid_sortable}" + echo "graph_title $(wp_get title "$n")" + echo "graph_order posts comments pingbacks" + echo "graph_vlabel Wordpress ID ${blogid_sortable}" + echo "posts.label Posts" + echo "comments.label Comments" + echo "pingbacks.label Pingbacks" + else + echo "multigraph wordpress.site_${blogid_sortable}" + echo "posts.value $(wp_get posts "$n")" + echo "comments.value $(wp_get comments "$n")" + echo "pingbacks.value $(wp_get pingbacks "$n")" + fi +done diff --git a/plugins/other/wordpress2 b/plugins/wordpress/wordpress2 old mode 100644 new mode 100755 similarity index 97% rename from plugins/other/wordpress2 rename to plugins/wordpress/wordpress2 index 81484fa8..c075b21a --- a/plugins/other/wordpress2 +++ b/plugins/wordpress/wordpress2 @@ -72,15 +72,12 @@ if(!empty($d["conf"]) && !empty($d["name"])) { $d["tbpf"] = $table_prefix; } -// In which category it should be displayed -$category = "other"; - if($argv[1] == "config") { echo 'graph_title wordpress statistic of '.$d["name"]."\n"; echo 'graph_order users posts comments pingbacks'."\n"; echo 'graph_vlabel Wordpress'."\n"; echo 'graph_info some wordpress statistics of '.$d["name"]."\n"; - echo 'graph_category '.$category."\n"; + echo 'graph_category cms'."\n"; echo 'users.label Users'."\n"; echo 'posts.label Posts'."\n"; echo 'posts.draw LINE3'."\n"; diff --git a/plugins/wowza/wowza-media-server b/plugins/wowza/wowza-media-server index d732687d..2cbb73cf 100755 --- a/plugins/wowza/wowza-media-server +++ b/plugins/wowza/wowza-media-server @@ -137,7 +137,7 @@ try: print ("graph_title Wowza clients listening duration") print ("graph_args --base 1000 -l 0") print ("graph_scale no") - print ("graph_category Wowza") + print ("graph_category streaming") print ("graph_vlabel minutes") print ("avg.label average listening duration") print ("mdn.label median listening duration") @@ -146,7 +146,7 @@ try: print ("graph_title Wowza listeners count by vhosts") print ("graph_args --base 1000 -l 0") print ("graph_scale no") - print ("graph_category Wowza") + print ("graph_category streaming") print ("graph_vlabel listeners") is_first = True for vh in vhosts: @@ -162,7 +162,7 @@ try: print ("graph_title Wowza clients listening duration by vhosts") print ("graph_args --base 1000 -l 0") print ("graph_scale no") - print ("graph_category Wowza") + print ("graph_category streaming") print ("graph_vlabel minutes") for vh in vhosts: vname = vh["Name"].strip("/").replace(".","_").replace("-","_") @@ -173,7 +173,7 @@ try: print ("graph_title Wowza vhosts uptime") print ("graph_args --base 1000 -l 0") print ("graph_scale no") - print ("graph_category Wowza") + print ("graph_category streaming") print ("graph_vlabel hours") for vh in vhosts: vname = vh["Name"].strip("/").replace(".","_").replace("-","_") @@ -183,7 +183,7 @@ try: print ("graph_title Wowza listeners count by apps") print ("graph_args --base 1000 -l 0") print ("graph_scale no") - print ("graph_category Wowza") + print ("graph_category streaming") print ("graph_vlabel listeners") is_first = True for vh in vhosts: @@ -201,7 +201,7 @@ try: print ("graph_title Wowza clients listening duration by apps") print ("graph_args --base 1000 -l 0") print ("graph_scale no") - print ("graph_category Wowza") + print ("graph_category streaming") print ("graph_vlabel minutes") for vh in vhosts: vname = vh["Name"].strip("/").replace(".","_").replace("-","_") @@ -214,7 +214,7 @@ try: print ("graph_title Wowza apps uptime") print ("graph_args --base 1000 -l 0") print ("graph_scale no") - print ("graph_category Wowza") + print ("graph_category streaming") print ("graph_vlabel hours") for vh in vhosts: vname = vh["Name"].strip("/").replace(".","_").replace("-","_") @@ -226,7 +226,7 @@ try: print ("graph_title Wowza listeners count") print ("graph_args --base 1000 -l 0") print ("graph_scale no") - print ("graph_category Wowza") + print ("graph_category streaming") print ("graph_vlabel listeners") print ("wowza_listeners.label Total Listeners") print ("wowza_listeners.draw AREA") diff --git a/plugins/wuala/wuala_stats b/plugins/wuala/wuala_stats index 2cbe37bd..bab9827c 100755 --- a/plugins/wuala/wuala_stats +++ b/plugins/wuala/wuala_stats @@ -42,7 +42,7 @@ if ($ARGV[0] and $ARGV[0] eq "autoconf") if($0 =~ /.*_uptime/) { print "graph_args -l0 --vertical-label %\n"; print "graph_title Wuala Uptime\n"; - print "graph_category wuala\n"; + print "graph_category backup\n"; print "graph_info This graph shows the Wua.la uptime\n"; print "uptime.label Uptime\n"; print "uptime.draw LINE2\n"; @@ -50,7 +50,7 @@ if ($ARGV[0] and $ARGV[0] eq "autoconf") } elsif($0 =~ /.*_storage/) { print "graph_args --base 1024 -l 0 --vertical-label GB\n"; print "graph_title Wuala Storage\n"; - print "graph_category wuala\n"; + print "graph_category backup\n"; print "graph_info This graph shows several storage related statistics of Wua.la.\n"; print "limit.label Local Storage Limit\n"; print "limit.draw LINE2\n"; diff --git a/plugins/xastir/xastir b/plugins/xastir/xastir index c8944d6b..5894b181 100755 --- a/plugins/xastir/xastir +++ b/plugins/xastir/xastir @@ -47,7 +47,7 @@ case $1 in graph_title Xastir Packet Stats graph_vlabel Packets -graph_category ham_radio +graph_category radio graph_scale no graph_printf %.0lf diff --git a/plugins/xbnbt/xbnbt_peers b/plugins/xbnbt/xbnbt_peers index ff41e940..34d559ac 100755 --- a/plugins/xbnbt/xbnbt_peers +++ b/plugins/xbnbt/xbnbt_peers @@ -16,7 +16,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # -# Capabilites +# Capabilities # config # autoconf # @@ -51,7 +51,7 @@ if ( defined $ARGV[0] and $ARGV[0] eq "config" ) { print "graph_title xbnbt peers\n"; print "graph_args --base 1000 -l 0\n"; print "graph_vlabel peers\n"; - print "graph_category xbnbt\n"; + print "graph_category filetransfer\n"; print "peers.label peers\n"; print "seeds.label seeds\n"; print "leechers.label leechers\n"; diff --git a/plugins/virtualization/xen b/plugins/xen/xen similarity index 96% rename from plugins/virtualization/xen rename to plugins/xen/xen index 32d702c1..b44f2692 100755 --- a/plugins/virtualization/xen +++ b/plugins/xen/xen @@ -15,7 +15,7 @@ #%# capabilities=autoconf # statefile: name of seen xen domains -statefile="/var/lib/munin/plugin-state/munin-plugin-xen.state" +statefile="$MUNIN_PLUGSTATE/munin-plugin-xen.state" if [ "$1" = "autoconf" ]; then if which xm > /dev/null ; then diff --git a/plugins/virtualization/xen-cpu b/plugins/xen/xen-cpu similarity index 100% rename from plugins/virtualization/xen-cpu rename to plugins/xen/xen-cpu diff --git a/plugins/virtualization/xen-multi b/plugins/xen/xen-multi similarity index 100% rename from plugins/virtualization/xen-multi rename to plugins/xen/xen-multi diff --git a/plugins/virtualization/xen_cpu_v2 b/plugins/xen/xen_cpu_v2 similarity index 96% rename from plugins/virtualization/xen_cpu_v2 rename to plugins/xen/xen_cpu_v2 index 75c9a951..aa58184c 100755 --- a/plugins/virtualization/xen_cpu_v2 +++ b/plugins/xen/xen_cpu_v2 @@ -171,8 +171,12 @@ while (($key, $value) = each %dom) # Calc a percentage based on the _total_ available CPU time $tmp = 0; - $tmp = ( $dom{$key}->{'diff'} / $tcpuavail ) * 100; - $dom{$key}->{'pc_tcpu'} = sprintf("%.2f", $tmp); + if ($tcpuavail != 0) { + $tmp = ( $dom{$key}->{'diff'} / $tcpuavail ) * 100; + $dom{$key}->{'pc_tcpu'} = sprintf("%.2f", $tmp); + } else { + $dom{$key}->{'pc_tcpu'} = "U"; + } if ( $debug ) { diff --git a/plugins/virtualization/xen_memory b/plugins/xen/xen_memory similarity index 100% rename from plugins/virtualization/xen_memory rename to plugins/xen/xen_memory diff --git a/plugins/virtualization/xen_traffic_ b/plugins/xen/xen_traffic_ similarity index 100% rename from plugins/virtualization/xen_traffic_ rename to plugins/xen/xen_traffic_ diff --git a/plugins/virtualization/xen_traffic_all b/plugins/xen/xen_traffic_all similarity index 100% rename from plugins/virtualization/xen_traffic_all rename to plugins/xen/xen_traffic_all diff --git a/plugins/virtualization/xen_vbd b/plugins/xen/xen_vbd similarity index 100% rename from plugins/virtualization/xen_vbd rename to plugins/xen/xen_vbd diff --git a/plugins/other/yum_activity b/plugins/yum/yum_activity similarity index 96% rename from plugins/other/yum_activity rename to plugins/yum/yum_activity index d2673ac5..bad9a4dd 100755 --- a/plugins/other/yum_activity +++ b/plugins/yum/yum_activity @@ -23,6 +23,7 @@ if [ "$1" = "config" ]; then echo 'graph_title Yum Activity' echo 'graph_args --base 1000 -l 0 ' + echo 'graph_category security' echo 'graph_vlabel Yum Activity' echo 'updates.label Yum Updated' echo 'updates.draw AREA' diff --git a/plugins/zfs/example-graphs/zfs_arcstats-1.png b/plugins/zfs/example-graphs/zfs_arcstats-1.png new file mode 100644 index 00000000..18187abc Binary files /dev/null and b/plugins/zfs/example-graphs/zfs_arcstats-1.png differ diff --git a/plugins/zfs/example-graphs/zfs_arcstats-2.png b/plugins/zfs/example-graphs/zfs_arcstats-2.png new file mode 100644 index 00000000..ab3204ab Binary files /dev/null and b/plugins/zfs/example-graphs/zfs_arcstats-2.png differ diff --git a/plugins/zfs/example-graphs/zpool_iostat-month.png b/plugins/zfs/example-graphs/zpool_iostat-month.png new file mode 100644 index 00000000..c03297e4 Binary files /dev/null and b/plugins/zfs/example-graphs/zpool_iostat-month.png differ diff --git a/plugins/zfs/example-graphs/zpool_iostat-week.png b/plugins/zfs/example-graphs/zpool_iostat-week.png new file mode 100644 index 00000000..9ce94ef2 Binary files /dev/null and b/plugins/zfs/example-graphs/zpool_iostat-week.png differ diff --git a/plugins/zfs/zfs-filesystem-graph b/plugins/zfs/zfs-filesystem-graph index 7a3d1c3d..a3bd62d6 100755 --- a/plugins/zfs/zfs-filesystem-graph +++ b/plugins/zfs/zfs-filesystem-graph @@ -41,7 +41,7 @@ if [ "$1" = "config" ]; then graph_order usedbydataset usedbychildren usedbysnapshots usedbyrefreservation available total quota graph_args --base 1024 -r -l 0 --vertical-label Bytes graph_info This graph shows how is used a zfs filesystems. -graph_category filesystem +graph_category fs graph_period second usedbydataset.label UsedByDataset usedbydataset.draw AREA diff --git a/plugins/zfs/zfs-stats-for-freebsd-arc-efficiency b/plugins/zfs/zfs-stats-for-freebsd-arc-efficiency index c14c895b..178c7000 100755 --- a/plugins/zfs/zfs-stats-for-freebsd-arc-efficiency +++ b/plugins/zfs/zfs-stats-for-freebsd-arc-efficiency @@ -38,7 +38,7 @@ if [ "$1" = "config" ]; then echo 'graph_title ZFS ARC Efficiency' echo 'graph_args -l 0' echo 'graph_vlabel %' - echo 'graph_category filesystem' + echo 'graph_category fs' echo 'graph_info This graph shows the ARC Efficiency' /usr/local/bin/zfs-stats -A | awk 'BEGIN { RS = "" ; FS = "\n" } /Efficiency/ {print}' | \ diff --git a/plugins/zfs/zfs-stats-for-freebsd-arc-utilization b/plugins/zfs/zfs-stats-for-freebsd-arc-utilization index 87a64a03..266ddaf0 100755 --- a/plugins/zfs/zfs-stats-for-freebsd-arc-utilization +++ b/plugins/zfs/zfs-stats-for-freebsd-arc-utilization @@ -38,7 +38,7 @@ if [ "$1" = "config" ]; then echo 'graph_title ZFS ARC Size' echo 'graph_args -l 0' echo 'graph_vlabel Size in MB' - echo 'graph_category filesystem' + echo 'graph_category fs' echo 'graph_info This graph shows the ARC Size utilization' /usr/local/bin/zfs-stats -A | awk 'BEGIN { RS = "" ; FS = "\n" } /^ARC Size/ {print}' | \ diff --git a/plugins/zfs/zfs-stats-for-freebsd-cache-hits-by-cache-list b/plugins/zfs/zfs-stats-for-freebsd-cache-hits-by-cache-list index 0273d3f3..3e9bbb51 100755 --- a/plugins/zfs/zfs-stats-for-freebsd-cache-hits-by-cache-list +++ b/plugins/zfs/zfs-stats-for-freebsd-cache-hits-by-cache-list @@ -38,7 +38,7 @@ if [ "$1" = "config" ]; then echo 'graph_title ZFS ARC Cache Hits by Cache List' echo 'graph_args -l 0' echo 'graph_vlabel %' - echo 'graph_category filesystem' + echo 'graph_category fs' echo 'graph_info This graph shows the ARC cache hits by cache list' /usr/local/bin/zfs-stats -A | awk 'BEGIN { RS = "" ; FS = "\n" } /CACHE HITS BY CACHE LIST/ {print}' | \ diff --git a/plugins/zfs/zfs-stats-for-freebsd-cache-hits-by-data-type b/plugins/zfs/zfs-stats-for-freebsd-cache-hits-by-data-type index 4dd0b923..030f0f42 100755 --- a/plugins/zfs/zfs-stats-for-freebsd-cache-hits-by-data-type +++ b/plugins/zfs/zfs-stats-for-freebsd-cache-hits-by-data-type @@ -38,7 +38,7 @@ if [ "$1" = "config" ]; then echo 'graph_title ZFS ARC Cache Hits and Misses' echo 'graph_args --upper-limit 100 -l -100' echo 'graph_vlabel %' - echo 'graph_category filesystem' + echo 'graph_category fs' echo 'graph_info This graph shows the ARC cache hits and misses by data type' /usr/local/bin/zfs-stats -A | awk 'BEGIN { RS = "" ; FS = "\n" } /HITS BY DATA TYPE/ {print}' | \ diff --git a/plugins/zfs/zfs-stats-for-freebsd-dmu-prefetch b/plugins/zfs/zfs-stats-for-freebsd-dmu-prefetch index f089e966..bbe8b652 100755 --- a/plugins/zfs/zfs-stats-for-freebsd-dmu-prefetch +++ b/plugins/zfs/zfs-stats-for-freebsd-dmu-prefetch @@ -38,7 +38,7 @@ if [ "$1" = "config" ]; then echo 'graph_title ZFS DMU prefech stats' echo 'graph_args --upper-limit 100 -l -100' echo 'graph_vlabel %' - echo 'graph_category filesystem' + echo 'graph_category fs' echo 'graph_info This graph shows the DMU prefech stats' /usr/local/bin/zfs-stats -Z | awk '/Hit Ratio/ {sub(/[\t ]*/,"");sub(/:.*/,""); displayname = $0; gsub(/[ .]/,"_",$0); print $0".label "displayname"\n"$0".min 0"}' diff --git a/plugins/zfs/zfs_arcstats b/plugins/zfs/zfs_arcstats new file mode 100755 index 00000000..7b0d74eb --- /dev/null +++ b/plugins/zfs/zfs_arcstats @@ -0,0 +1,361 @@ +#!/bin/bash + +: << =cut + +=head1 NAME + + zfs_arcstats - Munin multi-graph plugin to monitor ZFS ARC statistics + + These functions are implemented: + size : to monitor ARC size + activity : to monitor ARC activities + actlist : to monitor ARC activities by cache list (MFU/MRU) + actdata : to monitor ARC activities by data type (Demand/Prefetch) + hitratio : to monitor ARC hit ratio + + Tested with Solaris 10 and 11, OpenIndiana Hipster, FreeBSD 11, CentOS 7 + This plugin is inspired by arcstat.pl [https://github.com/mharsch/arcstat] + +=head1 CONFIGURATION + + Make symlink: + cd /path/to/munin/etc/plugins + ln -s /path/to/munin/lib/plugins/zfs_arcstats . + + For FreeBSD, it should be necessary to change shebang /bin/bash -> /usr/local/bin/bash + +=head1 ENVIRONMENT VARIABLES + + None + +=head1 AUTHOR + + K.Cima https://github.com/shakemid + +=head1 LICENSE + + GPLv2 + +=head1 Magic markers + + #%# family=contrib + #%# capabilities=autoconf + +=cut + +# Include plugin.sh +. "${MUNIN_LIBDIR:-}/plugins/plugin.sh" +is_multigraph "$@" + +# Shell options +set -o nounset + +# Set global variables +plugin_name=zfs_arcstats +functions='size activity actlist actdata hitratio' + +# Functions + +get_osname() { + local osname osver + + osname=$( uname -s ) + osver=$( uname -v ) + + case $osname in + SunOS) + case $osver in + illumos*) + osname=illumos + ;; + esac + ;; + esac + + echo "$osname" +} + +preconfig() { + local func=$1 + + # data_attr format: field type draw label + # label can contain white-spaces. + + case $func in + size) + global_attr=" + graph_title ZFS ARC - Size + graph_category fs + graph_args --base 1024 --lower-limit 0 + graph_vlabel Bytes + graph_info ZFS ARC - Size + " + case $osname in + SunOS) + # For Solaris 10,11 + data_attr=" + data_size GAUGE AREASTACK Data size + prefetch_meta_size GAUGE AREASTACK Prefetch meta size + buf_size GAUGE AREASTACK Buf size + other_size GAUGE AREASTACK Other size + " + ;; + *) + # For illumos, FreeBSD, Linux (OpenZFS) + data_attr=" + data_size GAUGE AREASTACK Data size + metadata_size GAUGE AREASTACK Metadata size + hdr_size GAUGE AREASTACK Hdr size + other_size GAUGE AREASTACK Other size + mru_size GAUGE LINE MRU size + mfu_size GAUGE LINE MFU size + " + ;; + esac + data_attr=" + $data_attr + size GAUGE LINE ARC size + c GAUGE LINE Target size + p GAUGE LINE Target MRU size + " + ;; + activity) + global_attr=" + graph_title ZFS ARC - Activities + graph_category fs + graph_args --base 1000 --lower-limit 0 + graph_vlabel misses (-) / hits (+) per second + graph_info ZFS ARC - Activities + + hits.negative misses + l2_hits.negative l2_misses + " + data_attr=" + misses DERIVE LINE dummy + hits DERIVE LINE ARC + l2_misses DERIVE LINE dummy + l2_hits DERIVE LINE L2ARC + " + ;; + actlist) + global_attr=" + graph_title ZFS ARC - Activities by cache list + graph_category fs + graph_args --base 1000 --lower-limit 0 + graph_vlabel ghost hits (-) / hits (+) per second + graph_info ZFS ARC - Activities by cache list + + mfu_hits.negative mfu_ghost_hits + mru_hits.negative mru_ghost_hits + " + data_attr=" + mfu_ghost_hits DERIVE LINE dummy + mfu_hits DERIVE LINE MFU + mru_ghost_hits DERIVE LINE dummy + mru_hits DERIVE LINE MRU + " + ;; + actdata) + global_attr=" + graph_title ZFS ARC - Activities by data type + graph_category fs + graph_args --base 1000 --lower-limit 0 + graph_vlabel misses (-) / hits (+) per second + graph_info ZFS ARC - Activities by data type + + demand_data_hits.negative demand_data_misses + demand_metadata_hits.negative demand_metadata_misses + prefetch_data_hits.negative prefetch_data_misses + prefetch_metadata_hits.negative prefetch_metadata_misses + " + data_attr=" + demand_data_misses DERIVE LINE dummy + demand_data_hits DERIVE LINE D data + demand_metadata_misses DERIVE LINE dummy + demand_metadata_hits DERIVE LINE D meta + prefetch_data_misses DERIVE LINE dummy + prefetch_data_hits DERIVE LINE P data + prefetch_metadata_misses DERIVE LINE dummy + prefetch_metadata_hits DERIVE LINE P meta + " + ;; + hitratio) + global_attr=" + graph_title ZFS ARC - Hit ratio + graph_category fs + graph_args --base 1000 --lower-limit 0 --upper-limit 100 --rigid + graph_vlabel % hits + graph_info ZFS ARC - Hit ratio - The graph shows cache hit ratio between munin-update intervals (usually 5 minutes). + + hitratio.cdef hits,DUP,misses,+,/,100,* + l2_hitratio.cdef l2_hits,DUP,l2_misses,+,/,100,* + demand_data_hitratio.cdef demand_data_hits,DUP,demand_data_misses,+,/,100,* + demand_metadata_hitratio.cdef demand_metadata_hits,DUP,demand_metadata_misses,+,/,100,* + prefetch_data_hitratio.cdef prefetch_data_hits,DUP,prefetch_data_misses,+,/,100,* + prefetch_metadata_hitratio.cdef prefetch_metadata_hits,DUP,prefetch_metadata_misses,+,/,100,* + " + data_attr=" + hits DERIVE LINE dummy + misses DERIVE LINE dummy + l2_hits DERIVE LINE dummy + l2_misses DERIVE LINE dummy + demand_data_hits DERIVE LINE dummy + demand_data_misses DERIVE LINE dummy + demand_metadata_hits DERIVE LINE dummy + demand_metadata_misses DERIVE LINE dummy + prefetch_data_hits DERIVE LINE dummy + prefetch_data_misses DERIVE LINE dummy + prefetch_metadata_hits DERIVE LINE dummy + prefetch_metadata_misses DERIVE LINE dummy + hitratio GAUGE LINE2 ARC hits + l2_hitratio GAUGE LINE L2ARC hits + demand_data_hitratio GAUGE LINE Demand data hits + demand_metadata_hitratio GAUGE LINE Demand metadata hits + prefetch_data_hitratio GAUGE LINE Prefetch data hits + prefetch_metadata_hitratio GAUGE LINE Prefetch metadata hits + " + ;; + *) + echo "Unknown function: $func" + exit 1 + ;; + esac +} + +do_config() { + local func=$1 + local label_max_length=45 + local field type draw label + + preconfig "$func" + echo "multigraph ${plugin_name}_${func}" + + # print global attributes + echo "$global_attr" | sed -e 's/^ *//' -e '/^$/d' + + # print data source attributes + echo "$data_attr" | while read -r field type draw label + do + [ -z "$field" ] && continue + + echo "${field}.type ${type}" + echo "${field}.draw ${draw}" + echo "${field}.label ${label:0:${label_max_length}}" + if [ "$type" = 'DERIVE' ]; then + echo "${field}.min 0" + fi + if [ "$label" = 'dummy' ]; then + echo "${field}.graph no" + fi + done + + echo +} + +get_stats() { + local arcstats stat value + + case $osname in + SunOS|illumos) + arcstats=$( kstat -p 'zfs:0:arcstats' | sed -e 's/:/ /g' | awk '{ print $4,$5 }' ) + # kstat output example: + # $ kstat -p zfs:0:arcstats + # zfs:0:arcstats:c 4135233544 + # ... + ;; + *BSD) + arcstats=$( /sbin/sysctl -a | sed -n -e 's/^kstat\.zfs\.misc\.arcstats\.//p' | awk -F: '{ print $1,$2 }' ) + # sysctl output example: + # $ sysctl -a + # ... + # kstat.zfs.misc.arcstats.c: 632540160 + # ... + ;; + Linux) + arcstats=$( sed '1,2d' /proc/spl/kstat/zfs/arcstats | awk '{ print $1,$3 }' ) + # proc file output example: + # $ cat /proc/spl/kstat/zfs/arcstats + # ... + # name type data + # hits 4 62 + # ... + ;; + *) + echo "Unsupported OS: $osname" + exit 1 + esac + + while read -r stat value + do + printf -v "arcstats_${stat}" "%s" "$value" + # printf -v means indirect variable assignment (similar to eval) + done <<< "$arcstats" +} + +do_fetch() { + local func=$1 + local field type draw label value ref + + preconfig "$func" + echo "multigraph ${plugin_name}_${func}" + + echo "$data_attr" | while read -r field type draw label + do + [ -z "$field" ] && continue + + ref="arcstats_${field}" + value=${!ref:-0} + # ${!varname} means indirect evaluation (similar to eval) + + echo "${field}.value ${value}" + done + + echo +} + +autoconf() { + if [ -x /sbin/zfs ]; then + echo yes + else + echo "no (ZFS looks unavailable)" + fi +} + +config() { + local func + + for func in $functions + do + do_config "$func" + done +} + +fetch() { + local func + + get_stats + + for func in $functions + do + do_fetch "$func" + done +} + +# Main + +osname=$( get_osname ) + +case ${1:-} in +autoconf) + autoconf + ;; +config) + config + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = "1" ]; then fetch; fi + ;; +*) + fetch + ;; +esac + +exit 0 diff --git a/plugins/zfs/zfs_cache_efficiency b/plugins/zfs/zfs_cache_efficiency index 53d1ba21..e793ca6e 100755 --- a/plugins/zfs/zfs_cache_efficiency +++ b/plugins/zfs/zfs_cache_efficiency @@ -15,7 +15,7 @@ case $1 in graph_title ZFS ARC efficiency graph_vlabel % graph_scale no -graph_category filesystem +graph_category fs graph_args -l 0 graph_info FreeBSD ZFS ARC Utilisation - Counters graph_period minute diff --git a/plugins/zfs/zfs_list b/plugins/zfs/zfs_list index e8c7ed38..72134b74 100755 --- a/plugins/zfs/zfs_list +++ b/plugins/zfs/zfs_list @@ -38,7 +38,7 @@ graph_title $fsname usage graph_order usedbydataset usedbychildren usedbysnapshots usedbyrefreservation available total quota graph_args --base 1024 -r -l 0 --vertical-label Bytes --upper-limit ${values[6]} graph_info This graph shows how is used a zfs filesystems. -graph_category filesystem +graph_category fs graph_period second usedbydataset.label UsedByDataset usedbydataset.draw AREA diff --git a/plugins/zfs/zfs_stats_ b/plugins/zfs/zfs_stats_ index 6c3fb116..da6a304c 100755 --- a/plugins/zfs/zfs_stats_ +++ b/plugins/zfs/zfs_stats_ @@ -272,7 +272,7 @@ l2efficiency() { } -[ "$1" = "config" ] && echo "graph_category filesystem" +[ "$1" = "config" ] && echo "graph_category fs" case "$FUNCTION" in efficiency) diff --git a/plugins/zfs/zfs_usage_ b/plugins/zfs/zfs_usage_ index 86ae6fb2..e44caf12 100755 --- a/plugins/zfs/zfs_usage_ +++ b/plugins/zfs/zfs_usage_ @@ -160,6 +160,10 @@ sub do_collect { foreach my $line (split(/\n/, `$fsget` )) { my ($name, $key, $value, undef ) = (split(/\t/,$line)); + # try to preserve underscores: + # * duplicate existing ones + # * afterwards replace slashs with single ones + ($name =~ s/_/__/g); ($name =~ s/\//_/g); $filesystems->{$name}->{$key}=$value; } @@ -169,7 +173,11 @@ sub do_collect { sub do_config_fs { my ($fs) = @_; my $fs_slash = ($fs); + # try to restore underscores (see "do_collect" for the reverse operation): + # * substitute all underscores with slashs + # * afterwards transform *double* slashs back into a single underscore ($fs_slash =~ s/_/\//g); + ($fs_slash =~ s/\/\//_/g); if ( $fs ne $zpool ) { printf( "multigraph zfs_usage_%s.%s\n", @@ -183,7 +191,7 @@ sub do_config_fs { } print "graph_args --base 1024 --lower-limit 0 --rigid\n"; print "graph_vlabel bytes \n"; - print "graph_category filesystem\n"; + print "graph_category fs\n"; print "graph_order @order\n"; foreach my $key ( keys %{$filesystems->{$fs}}) { diff --git a/plugins/zfs/zfsarcstats-counters b/plugins/zfs/zfsarcstats-counters index d47a6862..594b3d31 100755 --- a/plugins/zfs/zfsarcstats-counters +++ b/plugins/zfs/zfsarcstats-counters @@ -13,7 +13,7 @@ case $1 in graph_title ZFS ARC Counters graph_vlabel per second graph_scale no -graph_category filesystem +graph_category fs graph_args -l 0 graph_info FreeBSD ZFS ARC Utilisation - Counters EOF diff --git a/plugins/zfs/zfsonlinux_stats_ b/plugins/zfs/zfsonlinux_stats_ index d96c8fd2..62f8c1b4 100755 --- a/plugins/zfs/zfsonlinux_stats_ +++ b/plugins/zfs/zfsonlinux_stats_ @@ -37,6 +37,7 @@ while read name type data do [[ $name =~ ^[0-9].* ]] && continue [[ $name == "name" ]] && continue + [[ $name == "" ]] && continue case $name in "hits" ) export ARC_HITS=$data @@ -327,7 +328,7 @@ l2efficiency() { } -[ "$1" = "config" ] && echo "graph_category filesystem" +[ "$1" = "config" ] && echo "graph_category fs" case "$FUNCTION" in efficiency) diff --git a/plugins/zfs/zlist b/plugins/zfs/zlist index 3892f683..161a23bc 100755 --- a/plugins/zfs/zlist +++ b/plugins/zfs/zlist @@ -4,7 +4,7 @@ if [ "$1" = "config" ]; then echo 'graph_title zpool list' echo 'graph_args --base 1000 -l 0' echo 'graph_vlabel zpool list' - echo 'graph_category filesystem' + echo 'graph_category fs' echo 'graph_scale no' echo 'graph_info This graph shows zpool list on the server.' zlist=`/sbin/zpool list -H|awk '{print $1}'` diff --git a/plugins/zfs/zpool_capacity b/plugins/zfs/zpool_capacity new file mode 100755 index 00000000..e8c7a3ce --- /dev/null +++ b/plugins/zfs/zpool_capacity @@ -0,0 +1,267 @@ +#!/bin/bash + +: << =cut + +=head1 NAME + + zpool_capacity - Munin plugin to monitor ZFS capacity + + These functions are implemented: + capacity : to monitor zpool capacity % + allocated : to monitor zpool allocated bytes + dedup : to monitor zpool dedup and compress ratio + + Tested with Solaris 10 and 11, OpenIndiana Hipster, FreeBSD 11, CentOS 7 + +=head1 CONFIGURATION + + Make symlink: + cd /path/to/munin/etc/plugins + ln -s /path/to/munin/lib/plugins/zpool_capacity . + + For FreeBSD, it should be necessary to change shebang /bin/bash -> /usr/local/bin/bash + + For Linux, root privilege is necessary to run zpool command. + [zpool_capacity] + user root + +=head1 ENVIRONMENT VARIABLES + + critical : default 90 + warning : default 80 + +=head1 AUTHOR + + K.Cima https://github.com/shakemid + +=head1 LICENSE + + GPLv2 + +=head1 Magic markers + + #%# family=contrib + #%# capabilities=autoconf + +=cut + +# Include plugin.sh +. "${MUNIN_LIBDIR:-}/plugins/plugin.sh" +is_multigraph "$@" + +# Shell options +set -o nounset + +# Global variables +plugin_name=zpool_capacity +functions='capacity allocated dedup' +zpool_cmd=/sbin/zpool +zfs_cmd=/sbin/zfs + +# Environment variables +: "${warning:=80}" +: "${critical:=90}" + +# Note: The performance of ZFS may significantly degrade when zpool capacity > 90% +# See also: https://docs.oracle.com/cd/E53394_01/html/E54801/zfspools-4.html + +# Functions + +preconfig() { + local func="$1" + local p c + + # data_attr format: field type draw label + # label can contain white-spaces. + data_attr= + + case $func in + capacity) + global_attr=" + graph_title ZFS storage pool - Capacity + graph_category fs + graph_args --base 1000 --lower-limit 0 --upper-limit 100 + graph_vlabel % allocated + graph_info ZFS storage pool - Capacity + warning ${warning} + critical ${critical} + " + for p in $pool_list + do + data_attr="${data_attr} + ${p} GAUGE LINE2 ${p}" + done + ;; + allocated) + global_attr=" + graph_title ZFS storage pool - Allocated bytes + graph_category fs + graph_args --base 1024 --lower-limit 0 + graph_vlabel Bytes + graph_info ZFS storage pool - Allocated bytes + " + c=0 + for p in $pool_list + do + data_attr="${data_attr} + ${p}_size GAUGE LINE ${p} size + ${p}_allocated GAUGE LINE2 ${p} allocated" + global_attr="${global_attr} + ${p}_size.colour COLOUR${c} + ${p}_allocated.colour COLOUR${c}" + c=$(( c + 1 )) + done + ;; + dedup) + global_attr=" + graph_title ZFS storage pool - Dedup and compress ratio + graph_category fs + graph_args --base 1000 --lower-limit 1 + graph_vlabel Ratio + graph_info ZFS storage pool - Dedup and compress ratio + " + for p in $pool_list + do + data_attr="${data_attr} + ${p}_dedup GAUGE LINE ${p} dedup + ${p}_compress GAUGE LINE ${p} compress" + done + ;; + esac +} + +do_config() { + local func="$1" + local label_max_length=45 + local field type draw label + + preconfig "$func" + echo "multigraph ${plugin_name}_${func}" + + # print global attributes + echo "$global_attr" | sed -e 's/^ *//' -e '/^$/d' + + # print data source attributes + echo "$data_attr" | while read -r field type draw label + do + [ -z "$field" ] && continue + + field=$( clean_fieldname "$field" ) + echo "${field}.type ${type}" + echo "${field}.draw ${draw}" + echo "${field}.label ${label:0:${label_max_length}}" + if [ "$type" = 'DERIVE' ]; then + echo "${field}.min 0" + fi + if [ "$label" = 'dummy' ]; then + echo "${field}.graph no" + fi + done + + echo +} + +get_stats() { + local func="$1" + + case $func in + capacity) + "$zpool_cmd" list -H -o name,capacity | sed 's/%$//' + ;; + allocated) + ( "$zpool_cmd" list -H -o name,allocated \ + | awk '{ print $1"_allocated", $2 }' + "$zpool_cmd" list -H -o name,size \ + | awk '{ print $1"_size", $2 }' + ) \ + | perl -ane ' + @unit{ qw/ K M G T P E / } = ( 1 .. 6 ); + $name = $F[0]; + $byteu = $F[1]; + ( $n, $u ) = $byteu =~ /^([\d.]+)([KMGTPE]?)$/; + $byte = int( $n * 1024 ** ( $u ? $unit{ $u } : 0 ) ); + print "$name $byte\n"; + ' + # Note: ZFS supports up to 16EB. + ;; + dedup) + "$zpool_cmd" list -H -o name,dedup \ + | sed 's/x$//' \ + | awk '{ print $1"_dedup", $2 }' + # example output: + # $ zpool list -H -o name,dedup + # rpool 1.00x + # ... + + "$zpool_cmd" list -H -o name \ + | xargs "$zfs_cmd" get -H -o name,value compressratio \ + | sed 's/x$//' \ + | awk '{ print $1"_compress", $2 }' + # example output: + # $ zfs get -H -o name,value compressratio rpool + # rpool 1.00x + ;; + esac +} + +do_fetch() { + local func="$1" + local zpool_stats field value + + # zpool_stats contains 'key value\n' + zpool_stats=$( get_stats "$func" ) + + echo "multigraph ${plugin_name}_${func}" + + echo "$zpool_stats" | while read -r field value + do + field=$( clean_fieldname "$field" ) + echo "${field}.value ${value}" + done + + echo +} + +autoconf() { + if [ -x "$zpool_cmd" ]; then + echo yes + else + echo "no (failed to find executable 'zpool')" + fi +} + +config() { + local func + + pool_list=$( "$zpool_cmd" list -H -o name ) + + for func in $functions + do + do_config "$func" + done +} + +fetch() { + local func + + for func in $functions + do + do_fetch "$func" + done +} + +# Main +case ${1:-} in +autoconf) + autoconf + ;; +config) + config + if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = "1" ]; then fetch; fi + ;; +*) + fetch + ;; +esac + +exit 0 diff --git a/plugins/zfs/zpool_iostat b/plugins/zfs/zpool_iostat index 51c606e6..0e655f58 100755 --- a/plugins/zfs/zpool_iostat +++ b/plugins/zfs/zpool_iostat @@ -1,48 +1,127 @@ -#!/bin/bash +#!/bin/sh +# -*- sh -*- -if [ "$1" = "autoconf" ]; then - echo yes - exit 0 +set -eu + +: <<=cut + +=head1 NAME + +zpool_iostat - Plugin to monitor transfer statistics of ZFS pools + +=head1 APPLICABLE SYSTEMS + +All systems with "zpool" installed. + +=head1 CONFIGURATION + +No configuration is required. + +=head1 INTERPRETATION + +This plugin shows a graph with read (positive) and write (negative) values +for the IO transfer of each pool. + +=head1 MAGIC MARKERS + + #%# family=auto + #%# capabilities=autoconf + +=head1 AUTHOR + +tsaavik +Peter Doherty +Lars Kruse + +=head1 LICENSE + +GPLv2 + +=cut + + +# shellcheck source=/usr/share/munin/plugins/plugin.sh +. "$MUNIN_LIBDIR/plugins/plugin.sh" + + +ZPOOL_BIN=/sbin/zpool +ACTION="${1:-}" + + +if [ "$ACTION" = "autoconf" ]; then + if [ -x "$ZPOOL_BIN" ]; then + echo yes + else + echo "no (missing executable '$ZPOOL_BIN')" + fi + exit 0 fi -zlines=`/sbin/zpool iostat -v| wc -l|sed 's/ //g'` -ztail=`echo "-"$zlines` -ztmp=/var/run/munin/zpool_iostat -zdata=`/sbin/zpool iostat -v 1 1| tail $ztail > $ztmp` -zlist=`cat $ztmp|gawk '/alloc/ {next}; /avail/ {next}; /raid/ {next}; /mirror/ {next}; { if ( $4 >=0 ) print $1}'` -zname=`cat $ztmp|gawk '/alloc/ {next}; /avail/ {next}; /raid/ {next}; /mirror/ {next}; { if ( $4 >=0 ) print $1}'|gawk '{gsub("[^a-zA-Z0-9_]", "_", $1); print}'` -zorder=`for o in $zname; do echo $o'_read '; echo $o'_write '; done` +zlines=$("$ZPOOL_BIN" iostat -v | wc -l | sed 's/ //g') +iostats=$("$ZPOOL_BIN" iostat -v 1 1 | tail "-$zlines") +zlist=$(echo "$iostats" \ + | gawk '/alloc/ {next}; /avail/ {next}; /raid/ {next}; /mirror/ {next}; + { if ( $4 >=0 ) print $1}' \ + | tr ' ' '\n') -if [ "$1" = "config" ]; then - echo 'graph_title zpool iostat' - echo 'graph_args --base 1000 -l 0' - echo 'graph_vlabel write - read KBytes/s' - echo 'graph_category filesystem' - echo 'graph_scale no' - echo 'graph_info This graph shows zpool iostat' - echo 'graph_order '$zorder - echo $zlist | tr ' ' '\n' | while read i; do - case $i in - *) name=`echo $i | gawk '{ gsub("[^a-zA-Z0-9_]", "_", $1); print }'` ;; - esac - echo $name'_read.label '$i - echo $name'_read.type GAUGE' - echo $name'_read.graph no' - echo $name'_write.label '$i - echo $name'_write.type GAUGE' - echo $name'_write.negative '$name'_read' - done - exit 0 +# Parse the n'th column of the iostat output for a given pool or disk as a +# number (interpreting K and M suffixes). +get_device_iostat_column() { + local device_label="$1" + local stat_column="$2" + # convert all numeric values into kB + echo "$iostats" \ + | gawk '{ if ($1 == "'"$device_label"'") print $'"$stat_column"'; }' \ + | gawk '/M/ {print strtonum($1)*1000}; + /K/ {print strtonum($1)}; + /[0-9]$/ {print int($1)/1000}' +} + + +get_device_fieldname() { + local device_id="$1" + # Backwards compatibility (until 2016): keep the unprefixed pool name + # for the fieldname, except for pool names starting with digits. + if echo "$device_id" | grep -q "^[0-9]"; then + clean_fieldname "_$device_id" + else + clean_fieldname "$device_id" + fi +} + + +if [ "$ACTION" = "config" ]; then + echo 'graph_title zpool iostat' + echo 'graph_args --base 1000 -l 0' + echo 'graph_vlabel write (-) / read (+) KBytes/s' + echo 'graph_category disk' + echo 'graph_scale no' + echo 'graph_info This graph shows zpool iostat' + # Assemble the "graph_order" as a sorted list of read/write pairs for + # each device. + printf "graph_order" + echo "$zlist" | while read -r device_id; do + fieldname="$(get_device_fieldname "$device_id")" + printf " %s_read %s_write" "$fieldname" "$fieldname" + done + # finalize the 'graph_order' with a newline + echo + # output all fields: write as negative numbers and read as positive + echo "$zlist" | while read -r device_id; do + fieldname="$(get_device_fieldname "$device_id")" + echo "${fieldname}_read.label $device_id" + echo "${fieldname}_read.type GAUGE" + echo "${fieldname}_read.graph no" + echo "${fieldname}_write.label $device_id" + echo "${fieldname}_write.type GAUGE" + echo "${fieldname}_write.negative ${fieldname}_read" + done + exit 0 fi -echo $zlist | tr ' ' '\n' | while read iz; do - zlabel=`echo $iz|gawk '{print $1}'` - case $iz in - *) name=`echo $iz | gawk '{ gsub("[^a-zA-Z0-9_]", "_", $1); print $1 }'` ;; - esac - echo -n $name'_read.value ' - grep '^[ ]*'$zlabel $ztmp|gawk '{print $6}'|gawk '/M/ {print strtonum($1)*1000}; /K/ {print strtonum($1)}; /[0-9]$/ {print int($1)/1000}' - echo -n $name'_write.value ' - grep '^[ ]*'$zlabel $ztmp|gawk '{print $7}'|gawk '/M/ {print strtonum($1)*1000}; /K/ {print strtonum($1)}; /[0-9]$/ {print int($1)/1000}' + + +echo "$zlist" | while read -r device_id; do + fieldname="$(get_device_fieldname "$device_id")" + echo "${fieldname}_read.value $(get_device_iostat_column "$device_id" 6)" + echo "${fieldname}_write.value $(get_device_iostat_column "$device_id" 7)" done - -rm $ztmp; touch $ztmp diff --git a/plugins/zimbra/zimbra-mailboxsizes b/plugins/zimbra/zimbra-mailboxsizes index 23395c30..11bfc66d 100755 --- a/plugins/zimbra/zimbra-mailboxsizes +++ b/plugins/zimbra/zimbra-mailboxsizes @@ -58,7 +58,7 @@ if [ "$1" = "config" ]; then echo 'graph_args --base 1024 -l 0 ' echo 'graph_scale yes' echo 'graph_vlabel Mailboxsize' - echo 'graph_category Zimbra' + echo 'graph_category mail' /opt/zimbra/bin/zmprov gqu $(hostname)|awk '$3>102400 {sub (/@.*/, "",$1); sub(/\./, "_" ,$1); print $1".label "$1}' exit 0 fi diff --git a/plugins/znc/example-graphs/znc_logs-day.png b/plugins/znc/example-graphs/znc_logs.py-day.png similarity index 100% rename from plugins/znc/example-graphs/znc_logs-day.png rename to plugins/znc/example-graphs/znc_logs.py-day.png diff --git a/plugins/znc/znc_logs.py b/plugins/znc/znc_logs.py old mode 100644 new mode 100755 index 26eee0cc..75a9671b --- a/plugins/znc/znc_logs.py +++ b/plugins/znc/znc_logs.py @@ -11,7 +11,9 @@ Shows lines/minute in today's znc-logs [znc_logs] user znc # or any other user/group that can read the znclog-folder group znc -env.logdir /var/lib/znc/moddata/log/ # path to the log-folder with a "/" at the end +env.logdir /var/lib/znc/moddata/log/ # path to the GLOBAL log-folder with a "/" at the end +env.expire 0 # Keep channel names forever - OR - +env.expire 1 # Forget channel names from last run =head1 COPYRIGHT GPL VERSION 3 @@ -19,67 +21,193 @@ GPL VERSION 3 =head1 AUTHOR Thor77 ''' -from sys import argv -from time import strftime -from os import environ, listdir +import json +import os, sys, time +import re +import stat +import traceback -logdir = environ.get('logdir') +logdir = os.environ.get('logdir') +expire = os.environ.get('expire', 0) if not logdir: raise Exception('You have to set the logdir with env.logdir in the plugin-conf!') -date = strftime('%Y%m%d') -last_values_file = environ['MUNIN_PLUGSTATE'] + '/last_values' - +date = time.strftime('%Y%m%d') +longdate = time.strftime('%Y-%m-%d') +last_values_file = os.environ['MUNIN_PLUGSTATE'] + '/znc_logs_last' def get_last(): try: d = {} with open(last_values_file, 'r') as f: - for line in f: - line = line[:-1] - key, value = line.split(':') - d[key] = float(value) + d = json.load(f) return d except FileNotFoundError: return {} +def tail_open(filename, position=0): + # Based on tail_open from perls' Munin::Plugin + filereset = 0 + size = os.stat(filename)[stat.ST_SIZE] + if size is None: + return (undef, undef) + f = open(filename, 'r', encoding='utf-8', errors='replace') + if position > size: + filereset = 1 + else: + f.seek(position, 0) + newpos = f.tell() + if newpos != position: + raise Exception + return (f, filereset) -def data(): - last = get_last() - current = {} - for filename in listdir(logdir): - filename_ = filename.replace('.log', '') - network, channel, file_date = filename_.split('_') - network_channel = '{}_{}'.format(network, channel) - # check if log is from today and it is a channel - if file_date == date and channel.startswith('#'): - # current lines in the file - current_value = sum(1 for i in open(logdir + filename, 'r', encoding='utf-8', errors='replace')) +def tail_close(fh): + position = fh.tell() + fh.close() + return position - if network_channel not in last: - value = 0 - else: - last_value = last[network_channel] - # what munin gets - value = (current_value - last_value) / 5 # subtrate last from current and divide through 5 to get new lines / minute - if value < 0: - value = 0 - # save it to the states-file - current[network_channel] = current_value + +last = get_last() +if "users" in last: + user_list = last["users"] +else: + user_list = {} +if "channels" in last: + channel_list = last["channels"] +else: + channel_list = {} +if "log_pos" in last: + log_pos = last["log_pos"] +else: + log_pos = {} + +channel_stats = {} +user_stats = {} + +def read_data(savestate=True): + # Version 1.6 will change to directory-based filing, so walk recursively + for (dirpath, dirnames, filenames) in os.walk(logdir): + for filename in filenames: + filename_ = filename.replace('.log', '') + + user, network, channel, file_date = (None, None, None, None) + + try: + if len(dirpath) > len(logdir): + # We're below the log path, so this is a 1.6-style log + reldir = dirpath.replace(logdir + "/", '', 1) + try: + network, channel = reldir.split(os.sep) + except ValueError as e: + user, network, channel = reldir.split(os.sep) + file_date = filename_ + else: + try: + network, channel, file_date = filename_.split('_') + except ValueError as e: + user, network, channel, file_date = filename_.split('_') + except ValueError as e: + continue + network_channel = '{}@{}'.format(channel, network) + if network.lower() not in channel_list: + channel_list[network.lower()] = {} + if channel.startswith('#'): + channel_list[network.lower()][channel.lower()] = network_channel + user_list[user.lower()] = user + # check if log is from today + if (file_date == date or file_date == longdate): + # current lines in the file + (fh, r) = tail_open(os.path.join(dirpath,filename), log_pos.get(os.path.join(dirpath, filename), 0)) + current_value = 0 + while True: + where = fh.tell() + line = fh.readline() + if line.endswith('\n'): + current_value += 1 + else: + # Incomplete last line + fh.seek(where, 0) + log_pos[os.path.join(dirpath, filename)] = tail_close(fh) + break + + if network_channel.lower() in channel_stats and channel.startswith('#'): + channel_stats[network_channel.lower()] += current_value + else: + channel_stats[network_channel.lower()] = current_value + + if user is not None and user.lower() in user_stats: + user_stats[user.lower()] += current_value + else: + user_stats[user.lower()] = current_value + if savestate: + savedata = {} + if int(expire) == 0: + savedata["users"] = user_list + savedata["channels"] = channel_list + savedata["log_pos"] = log_pos + with open(last_values_file, 'w') as f: + json.dump(savedata,f) + + +def emit_config(): + print('graph_title Lines in the ZNC-log') + print('graph_category chat') + print('graph_vlabel lines / ${graph_period}') + print('graph_scale no') + print('graph_args --base 1000 --lower-limit 0') + print('graph_period minute') + graph_order = [] + + if os.getenv('MUNIN_CAP_DIRTYCONFIG') == "1": + read_data(1) + else: + read_data(0) + + for network in channel_list.keys(): + for channel in channel_list[network].keys(): # print things to munin - network_channel = network_channel.replace('.', '').replace('#', '') - print('{network_channel}.label {channel}@{network}'.format(network_channel=network_channel, channel=channel, network=network)) - print('{network_channel}.value {value}'.format(network_channel=network_channel, value=value)) - with open(last_values_file, 'w') as f: - for k in current: - f.write('{}:{}\n'.format(k, current[k])) + network_channel = "{}_{}".format(network,channel).replace('.', '').replace('#', '').replace('@','_') + print('{network_channel}.label {label}'.format(network_channel=network_channel, label=channel_list[network][channel])) + print('{network_channel}.type ABSOLUTE'.format(network_channel=network_channel)) + print('{network_channel}.min 0'.format(network_channel=network_channel)) + print('{network_channel}.draw AREASTACK'.format(network_channel=network_channel)) + + graph_order.append(network_channel) + for user in user_list.keys(): + fuser = re.sub(r'^[^A-Za-z_]', '_', user) + fuser = re.sub(r'[^A-Za-z0-9_]', '_', fuser) + print('{fuser}.label User {user}'.format(fuser=fuser, user=user)) + print('{fuser}.type ABSOLUTE'.format(fuser=fuser)) + print('{fuser}.min 0'.format(fuser=fuser)) + print('{fuser}.draw LINE1'.format(fuser=fuser)) + + print('graph_order {}'.format(" ".join(sorted(graph_order, key=str.lower)))) + +def emit_values(): + read_data(1) + for network in channel_list.keys(): + for channel in channel_list[network].keys(): + + # print things to munin + key = channel_list[network][channel] + network_channel = "{}_{}".format(network,channel).replace('.', '').replace('#', '').replace('@','_') + if key.lower() in channel_stats: + print('{network_channel}.value {value}'.format(network_channel=network_channel, value=channel_stats[key.lower()])) + else: + print('{network_channel}.value U'.format(network_channel=network_channel)) + for user in user_list.keys(): + fuser = re.sub(r'^[^A-Za-z_]', '_', user) + fuser = re.sub(r'[^A-Za-z0-9_]', '_', fuser) + if user.lower() in user_stats: + print('{fuser}.value {value}'.format(fuser=fuser, value=user_stats[user.lower()])) + else: + print('{fuser}.value U'.format(fuser=fuser)) -if len(argv) > 1 and argv[1] == 'config': - print('graph_title Lines in the ZNC-log') - print('graph_category znc') - print('graph_vlabel lines/minute') - print('graph_scale no') -data() +if len(sys.argv) > 1 and sys.argv[1] == 'config': + emit_config() + sys.exit(0) + +emit_values() diff --git a/plugins/zope/zodb/README b/plugins/zope/README-zodb similarity index 100% rename from plugins/zope/zodb/README rename to plugins/zope/README-zodb diff --git a/plugins/zope/zodb/scripts_python/munin_cache_parameters.py b/plugins/zope/scripts_zodb/munin_cache_parameters.py similarity index 100% rename from plugins/zope/zodb/scripts_python/munin_cache_parameters.py rename to plugins/zope/scripts_zodb/munin_cache_parameters.py diff --git a/plugins/zope/zodb/scripts_python/munin_db_activity.py b/plugins/zope/scripts_zodb/munin_db_activity.py similarity index 100% rename from plugins/zope/zodb/scripts_python/munin_db_activity.py rename to plugins/zope/scripts_zodb/munin_db_activity.py diff --git a/plugins/zeo/zeo_monitor_ b/plugins/zope/zeo_monitor_ similarity index 99% rename from plugins/zeo/zeo_monitor_ rename to plugins/zope/zeo_monitor_ index 4af76406..60b59f48 100755 --- a/plugins/zeo/zeo_monitor_ +++ b/plugins/zope/zeo_monitor_ @@ -136,7 +136,7 @@ if ( $ARGV[0] and $ARGV[0] eq "config") { graph_title ZEO $mode for storage $storage_name graph_args --base 1000 --lower-limit 0 graph_vlabel events per \${graph_period} -graph_category zeo +graph_category appserver graph_info ZEO performance monitoring EOF diff --git a/plugins/zeo/zeomonitor b/plugins/zope/zeomonitor similarity index 96% rename from plugins/zeo/zeomonitor rename to plugins/zope/zeomonitor index 4994a51b..a6620d88 100755 --- a/plugins/zeo/zeomonitor +++ b/plugins/zope/zeomonitor @@ -17,7 +17,7 @@ def config(data): print "graph_args -l 0" print "graph_vlabel n" print "graph_period minute" - print "graph_category ZEO" + print "graph_category appserver" def get_data(): import sys diff --git a/plugins/zope/zodb/zope_cache_parameters b/plugins/zope/zope_cache_parameters similarity index 97% rename from plugins/zope/zodb/zope_cache_parameters rename to plugins/zope/zope_cache_parameters index cfdf3813..a7b30700 100755 --- a/plugins/zope/zodb/zope_cache_parameters +++ b/plugins/zope/zope_cache_parameters @@ -17,7 +17,7 @@ if len(argv) > 1 and argv[1] == 'config': # as a percentage of target possibly.. print """graph_title Zope cache parameters - graph_category Zope + graph_category appserver graph_info A grap of the main data on the "Cache Parameters" tab of the main DB in the zope control panel.""".replace("\n ","\n") for i in range(0,len(conns)): print """obs_in_db%(i)s.label Z%(i)s Obs in DB diff --git a/plugins/zope/zope_conflict_errors b/plugins/zope/zope_conflict_errors index d496043e..59ad66a6 100755 --- a/plugins/zope/zope_conflict_errors +++ b/plugins/zope/zope_conflict_errors @@ -34,7 +34,7 @@ if len(argv) > 1: print """graph_title Zope Conflict Errors graph_vlabel Count - graph_category Zope + graph_category appserver graph_info The number of conflict errors in event logs over the past 24h""".replace("\n ","\n") for i in range(0,len(logs)): print """error_count%(i)s.label %(n)s error count"""% dict(i=i, diff --git a/plugins/zope/zodb/zope_db_activity b/plugins/zope/zope_db_activity similarity index 97% rename from plugins/zope/zodb/zope_db_activity rename to plugins/zope/zope_db_activity index 64175fb9..ddcbbef6 100755 --- a/plugins/zope/zodb/zope_db_activity +++ b/plugins/zope/zope_db_activity @@ -15,7 +15,7 @@ if len(argv) > 1 and argv[1] == 'config': print """graph_title Zope Database Activity graph_vlabel Count / 5 min. - graph_category Zope + graph_category appserver graph_info The number of Reads, Stores, and Connections that happens in the ZODB backend. The data comes from the same source as that in the "Recent Database Activity" tab in the zope control panel.""".replace("\n ","\n") for i in range(0,len(conns)): print """load_count%(i)s.label Z%(i)s Loads diff --git a/samples/munin.conf/Overviews b/samples/munin.conf/Overviews index 0ca55f70..9359bcb8 100644 --- a/samples/munin.conf/Overviews +++ b/samples/munin.conf/Overviews @@ -12,7 +12,7 @@ domain_order Quick Nodes Systems node_order Glassfish MongoDB [Quick;Glassfish] -# dont poll a node, we are loaning graphs from other RRDs +# don't poll a node, we are loaning graphs from other RRDs address 127.0.0.1 update no # define some "virtual" graph (loaned) diff --git a/t/test.t b/t/test.t index 7bc55d0b..db30c126 100644 --- a/t/test.t +++ b/t/test.t @@ -48,7 +48,14 @@ sub process_file { my ( $file, $filename, $interpreter, $arguments ) = @_; use v5.10.1; - if ( $interpreter =~ m{/bin/sh} ) { + if ( ! -x $file ) { + # missing executable flag + diag( + sprintf("\nFile '%s' lacks executable permission bits. Maybe try 'chmod +x $file'?\n", + $file) + ); + } + elsif ( $interpreter =~ m{/bin/sh} ) { subtest $filename => sub { plan tests => 2; run_check( @@ -56,20 +63,42 @@ sub process_file { description => 'sh syntax check' } ); + my $checkbashisms_location = `command -v checkbashisms 2>/dev/null`; + chomp($checkbashisms_location); + my $command; + if ($checkbashisms_location ne "") { + # monkey-patch "checkbashisms" in order to allow "command -v" + # see https://unix.stackexchange.com/a/85250: "command -v" vs. which/hash/... + # see https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=733511 + my $run_modified_checkbashisms = q/sed 's#command\\\s+-\[\^p\]#command\s+-[^pvV]#'/ + . " '$checkbashisms_location' | perl - '$file'"; + $command = [ 'sh', '-c', $run_modified_checkbashisms ]; + } else { + # make sure that the non-confusing "checkbashisms not found" message is displayed + $command = [ 'checkbashisms', $file ]; + } run_check( - { command => [ 'checkbashisms', $file ], + { command => $command, description => 'checkbashisms' } ); }; } elsif ( $interpreter =~ m{/bin/ksh} ) { - run_check( - { command => [ 'ksh', '-n', $file ], - description => 'ksh syntax check', - filename => $filename - } - ); + subtest $filename => sub { + plan tests => 2; + run_check( + { command => [ 'ksh', '-n', $file ], + description => 'ksh syntax check', + filename => $filename + } + ); + run_check( + { command => [ 'shellcheck', $file ], + description => 'shellcheck' + } + ); + } } elsif ( $interpreter =~ m{bash} ) { run_check( @@ -79,6 +108,14 @@ sub process_file { } ); } + elsif ( $interpreter =~ m{/bin/zsh} ) { + run_check( + { command => [ 'zsh', '-n', $file ], + description => 'zsh syntax check', + filename => $filename + } + ); + } elsif ( $interpreter =~ m{perl} ) { my $command; if ( $arguments =~ m{-.*T}mx ) { diff --git a/templates/munstrap/static/css/bootstrap.min.css b/templates/munstrap/static/css/bootstrap.min.css index 679272d2..ed3905e0 100644 --- a/templates/munstrap/static/css/bootstrap.min.css +++ b/templates/munstrap/static/css/bootstrap.min.css @@ -1,7 +1,6 @@ /*! - * Bootstrap v3.1.1 (http://getbootstrap.com) - * Copyright 2011-2014 Twitter, Inc. + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */ - -/*! normalize.css v3.0.0 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background:0 0}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}@media print{*{text-shadow:none!important;color:#000!important;background:transparent!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100%!important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}select{background:#fff!important}.navbar{display:none}.table td,.table th{background-color:#fff!important}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table-bordered th,.table-bordered td{border:1px solid #ddd!important}}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:before,:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:62.5%;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#428bca;text-decoration:none}a:hover,a:focus{color:#2a6496;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);border:0}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:400;line-height:1;color:#999}h1,.h1,h2,.h2,h3,.h3{margin-top:20px;margin-bottom:10px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10px;margin-bottom:10px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:200;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}small,.small{font-size:85%}cite{font-style:normal}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-muted{color:#999}.text-primary{color:#428bca}a.text-primary:hover{color:#3071a9}.text-success{color:#3c763d}a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#428bca}a.bg-primary:hover{background-color:#3071a9}.bg-success{background-color:#dff0d8}a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-left:5px;padding-right:5px}dl{margin-top:0;margin-bottom:20px}dt,dd{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #999}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#999}blockquote footer:before,blockquote small:before,blockquote .small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0;text-align:right}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:''}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:'\00A0 \2014'}blockquote:before,blockquote:after{content:""}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;white-space:nowrap;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;word-break:break-all;word-wrap:break-word;color:#333;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}.row{margin-left:-15px;margin-right:-15px}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-left:15px;padding-right:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:0}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:0}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:0}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:0}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:0}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:0}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:0}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:0}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{max-width:100%;background-color:transparent}th{text-align:left}.table{width:100%;margin-bottom:20px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-child(odd)>td,.table-striped>tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}.table-hover>tbody>tr:hover>td,.table-hover>tbody>tr:hover>th{background-color:#f5f5f5}table col[class*=col-]{position:static;float:none;display:table-column}table td[class*=col-],table th[class*=col-]{position:static;float:none;display:table-cell}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#d9edf7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f2dede}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}@media (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;overflow-x:scroll;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd;-webkit-overflow-scrolling:touch}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{padding:0;margin:0;border:0;min-width:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=radio],input[type=checkbox]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=radio]:focus,input[type=checkbox]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{cursor:not-allowed;background-color:#eee;opacity:1}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}input[type=date]{line-height:34px}.form-group{margin-bottom:15px}.radio,.checkbox{display:block;min-height:20px;margin-top:10px;margin-bottom:10px;padding-left:20px}.radio label,.checkbox label{display:inline;font-weight:400;cursor:pointer}.radio input[type=radio],.radio-inline input[type=radio],.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox]{float:left;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{display:inline-block;padding-left:20px;margin-bottom:0;vertical-align:middle;font-weight:400;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}input[type=radio][disabled],input[type=checkbox][disabled],.radio[disabled],.radio-inline[disabled],.checkbox[disabled],.checkbox-inline[disabled],fieldset[disabled] input[type=radio],fieldset[disabled] input[type=checkbox],fieldset[disabled] .radio,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}textarea.input-sm,select[multiple].input-sm{height:auto}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-lg{height:46px;line-height:46px}textarea.input-lg,select[multiple].input-lg{height:auto}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.has-feedback .form-control-feedback{position:absolute;top:25px;right:0;display:block;width:34px;height:34px;line-height:34px;text-align:center}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;border-color:#3c763d;background-color:#dff0d8}.has-success .form-control-feedback{color:#3c763d}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;border-color:#8a6d3b;background-color:#fcf8e3}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;border-color:#a94442;background-color:#f2dede}.has-error .form-control-feedback{color:#a94442}.form-control-static{margin-bottom:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;padding-left:0;vertical-align:middle}.form-inline .radio input[type=radio],.form-inline .checkbox input[type=checkbox]{float:none;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .control-label,.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{margin-top:0;margin-bottom:0;padding-top:7px}.form-horizontal .radio,.form-horizontal .checkbox{min-height:27px}.form-horizontal .form-group{margin-left:-15px;margin-right:-15px}.form-horizontal .form-control-static{padding-top:7px}@media (min-width:768px){.form-horizontal .control-label{text-align:right}}.form-horizontal .has-feedback .form-control-feedback{top:0;right:15px}.btn{display:inline-block;margin-bottom:0;font-weight:400;text-align:center;vertical-align:middle;cursor:pointer;background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus{color:#333;text-decoration:none}.btn:active,.btn.active{outline:0;background-image:none;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;pointer-events:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default:hover,.btn-default:focus,.btn-default:active,.btn-default.active,.open .dropdown-toggle.btn-default{color:#333;background-color:#ebebeb;border-color:#adadad}.btn-default:active,.btn-default.active,.open .dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default[disabled],fieldset[disabled] .btn-default,.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled:active,.btn-default[disabled]:active,fieldset[disabled] .btn-default:active,.btn-default.disabled.active,.btn-default[disabled].active,fieldset[disabled] .btn-default.active{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#428bca;border-color:#357ebd}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active,.open .dropdown-toggle.btn-primary{color:#fff;background-color:#3276b1;border-color:#285e8e}.btn-primary:active,.btn-primary.active,.open .dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary[disabled],fieldset[disabled] .btn-primary,.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled:active,.btn-primary[disabled]:active,fieldset[disabled] .btn-primary:active,.btn-primary.disabled.active,.btn-primary[disabled].active,fieldset[disabled] .btn-primary.active{background-color:#428bca;border-color:#357ebd}.btn-primary .badge{color:#428bca;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success:hover,.btn-success:focus,.btn-success:active,.btn-success.active,.open .dropdown-toggle.btn-success{color:#fff;background-color:#47a447;border-color:#398439}.btn-success:active,.btn-success.active,.open .dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success[disabled],fieldset[disabled] .btn-success,.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled:active,.btn-success[disabled]:active,fieldset[disabled] .btn-success:active,.btn-success.disabled.active,.btn-success[disabled].active,fieldset[disabled] .btn-success.active{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info:hover,.btn-info:focus,.btn-info:active,.btn-info.active,.open .dropdown-toggle.btn-info{color:#fff;background-color:#39b3d7;border-color:#269abc}.btn-info:active,.btn-info.active,.open .dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info[disabled],fieldset[disabled] .btn-info,.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled:active,.btn-info[disabled]:active,fieldset[disabled] .btn-info:active,.btn-info.disabled.active,.btn-info[disabled].active,fieldset[disabled] .btn-info.active{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active,.open .dropdown-toggle.btn-warning{color:#fff;background-color:#ed9c28;border-color:#d58512}.btn-warning:active,.btn-warning.active,.open .dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-warning,.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled:active,.btn-warning[disabled]:active,fieldset[disabled] .btn-warning:active,.btn-warning.disabled.active,.btn-warning[disabled].active,fieldset[disabled] .btn-warning.active{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active,.open .dropdown-toggle.btn-danger{color:#fff;background-color:#d2322d;border-color:#ac2925}.btn-danger:active,.btn-danger.active,.open .dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger[disabled],fieldset[disabled] .btn-danger,.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled:active,.btn-danger[disabled]:active,fieldset[disabled] .btn-danger:active,.btn-danger.disabled.active,.btn-danger[disabled].active,fieldset[disabled] .btn-danger.active{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{color:#428bca;font-weight:400;cursor:pointer;border-radius:0}.btn-link,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#2a6496;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#999;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%;padding-left:0;padding-right:0}.btn-block+.btn-block{margin-top:5px}input[type=submit].btn-block,input[type=reset].btn-block,input[type=button].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;transition:height .35s ease}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px solid;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;font-size:14px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175);background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{text-decoration:none;color:#262626;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;outline:0;background-color:#428bca}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#999}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);cursor:not-allowed}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{left:auto;right:0}.dropdown-menu-left{left:0;right:auto}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#999}.dropdown-backdrop{position:fixed;left:0;right:0;bottom:0;top:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}@media (min-width:768px){.navbar-right .dropdown-menu{left:auto;right:0}.navbar-right .dropdown-menu-left{left:0;right:auto}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group>.btn:focus,.btn-group-vertical>.btn:focus{outline:0}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child>.btn:last-child,.btn-group>.btn-group:first-child>.dropdown-toggle{border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn-group:last-child>.btn:first-child{border-bottom-left-radius:0;border-top-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-left:8px;padding-right:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-left:12px;padding-right:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-bottom-left-radius:4px;border-top-right-radius:0;border-top-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-right-radius:0;border-top-left-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{float:none;display:table-cell;width:1%}.btn-group-justified>.btn-group .btn{width:100%}[data-toggle=buttons]>.btn>input[type=radio],[data-toggle=buttons]>.btn>input[type=checkbox]{display:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-left:0;padding-right:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=radio],.input-group-addon input[type=checkbox]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-bottom-right-radius:0;border-top-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-bottom-left-radius:0;border-top-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{margin-left:-1px}.nav{margin-bottom:0;padding-left:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#999}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#999;text-decoration:none;background-color:transparent;cursor:not-allowed}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eee;border-color:#428bca}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#555;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{text-align:center;margin-bottom:5px}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#fff;background-color:#428bca}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{text-align:center;margin-bottom:5px}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-right-radius:0;border-top-left-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{max-height:340px;overflow-x:visible;padding-right:15px;padding-left:15px;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-left:0;padding-right:0}}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;padding:15px;font-size:18px;line-height:20px;height:50px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;margin-right:15px;padding:9px 10px;margin-top:8px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}.navbar-nav.navbar-right:last-child{margin-right:-15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important}}.navbar-form{margin-left:-15px;margin-right:-15px;padding:10px 15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);margin-top:8px;margin-bottom:8px}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;padding-left:0;vertical-align:middle}.navbar-form .radio input[type=radio],.navbar-form .checkbox input[type=checkbox]{float:none;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}}@media (min-width:768px){.navbar-form{width:auto;border:0;margin-left:0;margin-right:0;padding-top:0;padding-bottom:0;-webkit-box-shadow:none;box-shadow:none}.navbar-form.navbar-right:last-child{margin-right:-15px}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-right-radius:0;border-top-left-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-left:15px;margin-right:15px}.navbar-text.navbar-right:last-child{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{background-color:#e7e7e7;color:#555}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#999}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#999}.navbar-inverse .navbar-nav>li>a{color:#999}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{background-color:#080808;color:#fff}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#999}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#999}.navbar-inverse .navbar-link:hover{color:#fff}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{content:"/\00a0";padding:0 5px;color:#ccc}.breadcrumb>.active{color:#999}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;line-height:1.42857143;text-decoration:none;color:#428bca;background-color:#fff;border:1px solid #ddd;margin-left:-1px}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-bottom-left-radius:4px;border-top-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-bottom-right-radius:4px;border-top-right-radius:4px}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{color:#2a6496;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:2;color:#fff;background-color:#428bca;border-color:#428bca;cursor:default}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#999;background-color:#fff;border-color:#ddd;cursor:not-allowed}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-bottom-left-radius:6px;border-top-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-bottom-right-radius:6px;border-top-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-bottom-left-radius:3px;border-top-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-bottom-right-radius:3px;border-top-right-radius:3px}.pager{padding-left:0;margin:20px 0;list-style:none;text-align:center}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#999;background-color:#fff;cursor:not-allowed}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}.label[href]:hover,.label[href]:focus{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#999}.label-default[href]:hover,.label-default[href]:focus{background-color:gray}.label-primary{background-color:#428bca}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#3071a9}.label-success{background-color:#5cb85c}.label-success[href]:hover,.label-success[href]:focus{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:hover,.label-info[href]:focus{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;color:#fff;line-height:1;vertical-align:baseline;white-space:nowrap;text-align:center;background-color:#999;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}a.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#428bca;background-color:#fff}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.container .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron{padding-left:60px;padding-right:60px}.jumbotron h1,.jumbotron .h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-left:auto;margin-right:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#428bca}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable{padding-right:35px}.alert-dismissable .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{background-color:#dff0d8;border-color:#d6e9c6;color:#3c763d}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{background-color:#d9edf7;border-color:#bce8f1;color:#31708f}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{background-color:#fcf8e3;border-color:#faebcc;color:#8a6d3b}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{background-color:#f2dede;border-color:#ebccd1;color:#a94442}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{overflow:hidden;height:20px;margin-bottom:20px;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#428bca;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;transition:width .6s ease}.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:40px 40px}.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media,.media-body{overflow:hidden;zoom:1}.media,.media .media{margin-top:15px}.media:first-child{margin-top:0}.media-object{display:block}.media-heading{margin:0 0 5px}.media>.pull-left{margin-right:10px}.media>.pull-right{margin-left:10px}.media-list{padding-left:0;list-style:none}.list-group{margin-bottom:20px;padding-left:0}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-right-radius:4px;border-top-left-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}a.list-group-item{color:#555}a.list-group-item .list-group-item-heading{color:#333}a.list-group-item:hover,a.list-group-item:focus{text-decoration:none;background-color:#f5f5f5}a.list-group-item.active,a.list-group-item.active:hover,a.list-group-item.active:focus{z-index:2;color:#fff;background-color:#428bca;border-color:#428bca}a.list-group-item.active .list-group-item-heading,a.list-group-item.active:hover .list-group-item-heading,a.list-group-item.active:focus .list-group-item-heading{color:inherit}a.list-group-item.active .list-group-item-text,a.list-group-item.active:hover .list-group-item-text,a.list-group-item.active:focus .list-group-item-text{color:#e1edf7}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,a.list-group-item-success:focus{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:hover,a.list-group-item-success.active:focus{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,a.list-group-item-info:focus{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:hover,a.list-group-item-info.active:focus{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,a.list-group-item-warning:focus{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,a.list-group-item-danger:focus{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-right-radius:3px;border-top-left-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group{margin-bottom:0}.panel>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-right-radius:3px;border-top-left-radius:3px}.panel>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-right-radius:3px;border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{border:0;margin-bottom:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px;overflow:hidden}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse .panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse .panel-body{border-top-color:#ddd}.panel-default>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#428bca}.panel-primary>.panel-heading{color:#fff;background-color:#428bca;border-color:#428bca}.panel-primary>.panel-heading+.panel-collapse .panel-body{border-top-color:#428bca}.panel-primary>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#428bca}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse .panel-body{border-top-color:#d6e9c6}.panel-success>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse .panel-body{border-top-color:#bce8f1}.panel-info>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse .panel-body{border-top-color:#faebcc}.panel-warning>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse .panel-body{border-top-color:#ebccd1}.panel-danger>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#ebccd1}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.5;filter:alpha(opacity=50)}button.close{padding:0;cursor:pointer;background:0 0;border:0;-webkit-appearance:none}.modal-open{overflow:hidden}.modal{display:none;overflow:auto;overflow-y:scroll;position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);transform:translate(0,-25%);-webkit-transition:-webkit-transform .3s ease-out;-moz-transition:-moz-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5);background-clip:padding-box;outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0;filter:alpha(opacity=0)}.modal-backdrop.in{opacity:.5;filter:alpha(opacity=50)}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5;min-height:16.42857143px}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:20px}.modal-footer{margin-top:15px;padding:19px 20px 20px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-left:5px;margin-bottom:0}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1030;display:block;visibility:visible;font-size:12px;line-height:1.4;opacity:0;filter:alpha(opacity=0)}.tooltip.in{opacity:.9;filter:alpha(opacity=90)}.tooltip.top{margin-top:-3px;padding:5px 0}.tooltip.right{margin-left:3px;padding:0 5px}.tooltip.bottom{margin-top:3px;padding:5px 0}.tooltip.left{margin-left:-3px;padding:0 5px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{bottom:0;left:5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;right:5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;left:5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;right:5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1010;display:none;max-width:276px;padding:1px;text-align:left;background-color:#fff;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);white-space:normal}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{margin:0;padding:8px 14px;font-size:14px;font-weight:400;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{border-width:10px;content:""}.popover.top>.arrow{left:50%;margin-left:-11px;border-bottom-width:0;border-top-color:#999;border-top-color:rgba(0,0,0,.25);bottom:-11px}.popover.top>.arrow:after{content:" ";bottom:1px;margin-left:-10px;border-bottom-width:0;border-top-color:#fff}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-left-width:0;border-right-color:#999;border-right-color:rgba(0,0,0,.25)}.popover.right>.arrow:after{content:" ";left:1px;bottom:-10px;border-left-width:0;border-right-color:#fff}.popover.bottom>.arrow{left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25);top:-11px}.popover.bottom>.arrow:after{content:" ";top:1px;margin-left:-10px;border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{content:" ";right:1px;border-right-width:0;border-left-color:#fff;bottom:-10px}.carousel{position:relative}.carousel-inner{position:relative;overflow:hidden;width:100%}.carousel-inner>.item{display:none;position:relative;-webkit-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;left:0;bottom:0;width:15%;opacity:.5;filter:alpha(opacity=50);font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-control.left{background-image:-webkit-linear-gradient(left,color-stop(rgba(0,0,0,.5) 0),color-stop(rgba(0,0,0,.0001) 100%));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1)}.carousel-control.right{left:auto;right:0;background-image:-webkit-linear-gradient(left,color-stop(rgba(0,0,0,.0001) 0),color-stop(rgba(0,0,0,.5) 100%));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1)}.carousel-control:hover,.carousel-control:focus{outline:0;color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;margin-top:-10px;margin-left:-10px;font-family:serif}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;margin-left:-30%;padding-left:0;list-style:none;text-align:center}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;border:1px solid #fff;border-radius:10px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0)}.carousel-indicators .active{margin:0;width:12px;height:12px;background-color:#fff}.carousel-caption{position:absolute;left:15%;right:15%;bottom:20px;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-15px;margin-left:-15px;font-size:30px}.carousel-caption{left:20%;right:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-footer:before,.modal-footer:after{content:" ";display:table}.clearfix:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.panel-body:after,.modal-footer:after{clear:both}.center-block{display:block;margin-left:auto;margin-right:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important;visibility:hidden!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table}tr.visible-xs{display:table-row!important}th.visible-xs,td.visible-xs{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table}tr.visible-sm{display:table-row!important}th.visible-sm,td.visible-sm{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table}tr.visible-md{display:table-row!important}th.visible-md,td.visible-md{display:table-cell!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table}tr.visible-lg{display:table-row!important}th.visible-lg,td.visible-lg{display:table-cell!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table}tr.visible-print{display:table-row!important}th.visible-print,td.visible-print{display:table-cell!important}}@media print{.hidden-print{display:none!important}} \ No newline at end of file + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled.focus,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled].focus,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled.focus,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled].focus,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled.focus,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled].focus,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled.focus,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled].focus,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled.focus,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled].focus,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled.focus,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled].focus,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:2;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:3;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.modal-header:after,.modal-header:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.modal-header:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} +/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/templates/munstrap/static/css/bootstrap.min.css.map b/templates/munstrap/static/css/bootstrap.min.css.map new file mode 100644 index 00000000..6c7fa40b --- /dev/null +++ b/templates/munstrap/static/css/bootstrap.min.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["less/normalize.less","less/print.less","bootstrap.css","dist/css/bootstrap.css","less/glyphicons.less","less/scaffolding.less","less/mixins/vendor-prefixes.less","less/mixins/tab-focus.less","less/mixins/image.less","less/type.less","less/mixins/text-emphasis.less","less/mixins/background-variant.less","less/mixins/text-overflow.less","less/code.less","less/grid.less","less/mixins/grid.less","less/mixins/grid-framework.less","less/tables.less","less/mixins/table-row.less","less/forms.less","less/mixins/forms.less","less/buttons.less","less/mixins/buttons.less","less/mixins/opacity.less","less/component-animations.less","less/dropdowns.less","less/mixins/nav-divider.less","less/mixins/reset-filter.less","less/button-groups.less","less/mixins/border-radius.less","less/input-groups.less","less/navs.less","less/navbar.less","less/mixins/nav-vertical-align.less","less/utilities.less","less/breadcrumbs.less","less/pagination.less","less/mixins/pagination.less","less/pager.less","less/labels.less","less/mixins/labels.less","less/badges.less","less/jumbotron.less","less/thumbnails.less","less/alerts.less","less/mixins/alerts.less","less/progress-bars.less","less/mixins/gradients.less","less/mixins/progress-bar.less","less/media.less","less/list-group.less","less/mixins/list-group.less","less/panels.less","less/mixins/panels.less","less/responsive-embed.less","less/wells.less","less/close.less","less/modals.less","less/tooltip.less","less/mixins/reset-text.less","less/popovers.less","less/carousel.less","less/mixins/clearfix.less","less/mixins/center-block.less","less/mixins/hide-text.less","less/responsive-utilities.less","less/mixins/responsive-visibility.less"],"names":[],"mappings":";;;;4EAQA,KACE,YAAA,WACA,yBAAA,KACA,qBAAA,KAOF,KACE,OAAA,EAaF,QAAA,MAAA,QAAA,WAAA,OAAA,OAAA,OAAA,OAAA,KAAA,KAAA,IAAA,QAAA,QAaE,QAAA,MAQF,MAAA,OAAA,SAAA,MAIE,QAAA,aACA,eAAA,SAQF,sBACE,QAAA,KACA,OAAA,EAQF,SAAA,SAEE,QAAA,KAUF,EACE,iBAAA,YAQF,SAAA,QAEE,QAAA,EAUF,YACE,cAAA,IAAA,OAOF,EAAA,OAEE,YAAA,IAOF,IACE,WAAA,OAQF,GACE,OAAA,MAAA,EACA,UAAA,IAOF,KACE,MAAA,KACA,WAAA,KAOF,MACE,UAAA,IAOF,IAAA,IAEE,SAAA,SACA,UAAA,IACA,YAAA,EACA,eAAA,SAGF,IACE,IAAA,MAGF,IACE,OAAA,OAUF,IACE,OAAA,EAOF,eACE,SAAA,OAUF,OACE,OAAA,IAAA,KAOF,GACE,OAAA,EAAA,mBAAA,YAAA,gBAAA,YACA,WAAA,YAOF,IACE,SAAA,KAOF,KAAA,IAAA,IAAA,KAIE,YAAA,UAAA,UACA,UAAA,IAkBF,OAAA,MAAA,SAAA,OAAA,SAKE,OAAA,EACA,KAAA,QACA,MAAA,QAOF,OACE,SAAA,QAUF,OAAA,OAEE,eAAA,KAWF,OAAA,wBAAA,kBAAA,mBAIE,mBAAA,OACA,OAAA,QAOF,iBAAA,qBAEE,OAAA,QAOF,yBAAA,wBAEE,QAAA,EACA,OAAA,EAQF,MACE,YAAA,OAWF,qBAAA,kBAEE,mBAAA,WAAA,gBAAA,WAAA,WAAA,WACA,QAAA,EASF,8CAAA,8CAEE,OAAA,KAQF,mBACE,mBAAA,YACA,gBAAA,YAAA,WAAA,YAAA,mBAAA,UASF,iDAAA,8CAEE,mBAAA,KAOF,SACE,QAAA,MAAA,OAAA,MACA,OAAA,EAAA,IACA,OAAA,IAAA,MAAA,OAQF,OACE,QAAA,EACA,OAAA,EAOF,SACE,SAAA,KAQF,SACE,YAAA,IAUF,MACE,eAAA,EACA,gBAAA,SAGF,GAAA,GAEE,QAAA,uFCjUF,aA7FI,EAAA,OAAA,QAGI,MAAA,eACA,YAAA,eACA,WAAA,cAAA,mBAAA,eACA,WAAA,eAGJ,EAAA,UAEI,gBAAA,UAGJ,cACI,QAAA,KAAA,WAAA,IAGJ,kBACI,QAAA,KAAA,YAAA,IAKJ,6BAAA,mBAEI,QAAA,GAGJ,WAAA,IAEI,OAAA,IAAA,MAAA,KC4KL,kBAAA,MDvKK,MC0KL,QAAA,mBDrKK,IE8KN,GDLC,kBAAA,MDrKK,ICwKL,UAAA,eCUD,GF5KM,GE2KN,EF1KM,QAAA,ECuKL,OAAA,ECSD,GF3KM,GCsKL,iBAAA,MD/JK,QCkKL,QAAA,KCSD,YFtKU,oBCiKT,iBAAA,eD7JK,OCgKL,OAAA,IAAA,MAAA,KD5JK,OC+JL,gBAAA,mBCSD,UFpKU,UC+JT,iBAAA,eDzJS,mBEkKV,mBDLC,OAAA,IAAA,MAAA,gBEjPD,WACA,YAAA,uBFsPD,IAAA,+CE7OC,IAAK,sDAAuD,4BAA6B,iDAAkD,gBAAiB,gDAAiD,eAAgB,+CAAgD,mBAAoB,2EAA4E,cAE7W,WACA,SAAA,SACA,IAAA,IACA,QAAA,aACA,YAAA,uBACA,WAAA,OACA,YAAA,IACA,YAAA,EAIkC,uBAAA,YAAW,wBAAA,UACX,2BAAW,QAAA,QAEX,uBDuPlC,QAAS,QCtPyB,sBFiPnC,uBEjP8C,QAAA,QACX,wBAAW,QAAA,QACX,wBAAW,QAAA,QACX,2BAAW,QAAA,QACX,yBAAW,QAAA,QACX,wBAAW,QAAA,QACX,wBAAW,QAAA,QACX,yBAAW,QAAA,QACX,wBAAW,QAAA,QACX,uBAAW,QAAA,QACX,6BAAW,QAAA,QACX,uBAAW,QAAA,QACX,uBAAW,QAAA,QACX,2BAAW,QAAA,QACX,qBAAW,QAAA,QACX,0BAAW,QAAA,QACX,qBAAW,QAAA,QACX,yBAAW,QAAA,QACX,0BAAW,QAAA,QACX,2BAAW,QAAA,QACX,sBAAW,QAAA,QACX,yBAAW,QAAA,QACX,sBAAW,QAAA,QACX,wBAAW,QAAA,QACX,uBAAW,QAAA,QACX,uBAAW,QAAA,QACX,uBAAW,QAAA,QACX,uBAAW,QAAA,QACX,+BAAW,QAAA,QACX,2BAAW,QAAA,QACX,yBAAW,QAAA,QACX,wBAAW,QAAA,QACX,8BAAW,QAAA,QACX,yBAAW,QAAA,QACX,0BAAW,QAAA,QACX,2BAAW,QAAA,QACX,uBAAW,QAAA,QACX,uBAAW,QAAA,QACX,6BAAW,QAAA,QACX,6BAAW,QAAA,QACX,8BAAW,QAAA,QACX,4BAAW,QAAA,QACX,yBAAW,QAAA,QACX,0BAAW,QAAA,QACX,sBAAW,QAAA,QACX,uBAAW,QAAA,QACX,uBAAW,QAAA,QACX,2BAAW,QAAA,QACX,wBAAW,QAAA,QACX,yBAAW,QAAA,QACX,uBAAW,QAAA,QACX,uBAAW,QAAA,QACX,yBAAW,QAAA,QACX,8BAAW,QAAA,QACX,6BAAW,QAAA,QACX,6BAAW,QAAA,QACX,+BAAW,QAAA,QACX,8BAAW,QAAA,QACX,gCAAW,QAAA,QACX,uBAAW,QAAA,QACX,8BAAW,QAAA,QACX,+BAAW,QAAA,QACX,iCAAW,QAAA,QACX,0BAAW,QAAA,QACX,6BAAW,QAAA,QACX,yBAAW,QAAA,QACX,uBAAW,QAAA,QACX,uBAAW,QAAA,QACX,wBAAW,QAAA,QACX,wBAAW,QAAA,QACX,uBAAW,QAAA,QACX,gCAAW,QAAA,QACX,gCAAW,QAAA,QACX,2BAAW,QAAA,QACX,uBAAW,QAAA,QACX,wBAAW,QAAA,QACX,uBAAW,QAAA,QACX,0BAAW,QAAA,QACX,+BAAW,QAAA,QACX,+BAAW,QAAA,QACX,wBAAW,QAAA,QACX,+BAAW,QAAA,QACX,gCAAW,QAAA,QACX,4BAAW,QAAA,QACX,6BAAW,QAAA,QACX,8BAAW,QAAA,QACX,0BAAW,QAAA,QACX,gCAAW,QAAA,QACX,4BAAW,QAAA,QACX,6BAAW,QAAA,QACX,gCAAW,QAAA,QACX,4BAAW,QAAA,QACX,6BAAW,QAAA,QACX,6BAAW,QAAA,QACX,8BAAW,QAAA,QACX,2BAAW,QAAA,QACX,6BAAW,QAAA,QACX,4BAAW,QAAA,QACX,8BAAW,QAAA,QACX,+BAAW,QAAA,QACX,mCAAW,QAAA,QACX,uBAAW,QAAA,QACX,uBAAW,QAAA,QACX,uBAAW,QAAA,QACX,2BAAW,QAAA,QACX,4BAAW,QAAA,QACX,+BAAW,QAAA,QACX,wBAAW,QAAA,QACX,2BAAW,QAAA,QACX,yBAAW,QAAA,QACX,0BAAW,QAAA,QACX,yBAAW,QAAA,QACX,6BAAW,QAAA,QACX,+BAAW,QAAA,QACX,0BAAW,QAAA,QACX,gCAAW,QAAA,QACX,+BAAW,QAAA,QACX,8BAAW,QAAA,QACX,kCAAW,QAAA,QACX,oCAAW,QAAA,QACX,sBAAW,QAAA,QACX,2BAAW,QAAA,QACX,uBAAW,QAAA,QACX,8BAAW,QAAA,QACX,4BAAW,QAAA,QACX,8BAAW,QAAA,QACX,6BAAW,QAAA,QACX,4BAAW,QAAA,QACX,0BAAW,QAAA,QACX,4BAAW,QAAA,QACX,qCAAW,QAAA,QACX,oCAAW,QAAA,QACX,kCAAW,QAAA,QACX,oCAAW,QAAA,QACX,wBAAW,QAAA,QACX,yBAAW,QAAA,QACX,wBAAW,QAAA,QACX,yBAAW,QAAA,QACX,4BAAW,QAAA,QACX,6BAAW,QAAA,QACX,4BAAW,QAAA,QACX,4BAAW,QAAA,QACX,8BAAW,QAAA,QACX,uBAAW,QAAA,QACX,wBAAW,QAAA,QACX,0BAAW,QAAA,QACX,sBAAW,QAAA,QACX,sBAAW,QAAA,QACX,uBAAW,QAAA,QACX,mCAAW,QAAA,QACX,uCAAW,QAAA,QACX,gCAAW,QAAA,QACX,oCAAW,QAAA,QACX,qCAAW,QAAA,QACX,yCAAW,QAAA,QACX,4BAAW,QAAA,QACX,yBAAW,QAAA,QACX,gCAAW,QAAA,QACX,8BAAW,QAAA,QACX,yBAAW,QAAA,QACX,wBAAW,QAAA,QACX,0BAAW,QAAA,QACX,6BAAW,QAAA,QACX,yBAAW,QAAA,QACX,uBAAW,QAAA,QACX,uBAAW,QAAA,QACX,wBAAW,QAAA,QACX,yBAAW,QAAA,QACX,yBAAW,QAAA,QACX,uBAAW,QAAA,QACX,8BAAW,QAAA,QACX,+BAAW,QAAA,QACX,gCAAW,QAAA,QACX,8BAAW,QAAA,QACX,8BAAW,QAAA,QACX,8BAAW,QAAA,QACX,2BAAW,QAAA,QACX,0BAAW,QAAA,QACX,yBAAW,QAAA,QACX,6BAAW,QAAA,QACX,2BAAW,QAAA,QACX,4BAAW,QAAA,QACX,wBAAW,QAAA,QACX,wBAAW,QAAA,QACX,2BAAW,QAAA,QACX,2BAAW,QAAA,QACX,4BAAW,QAAA,QACX,+BAAW,QAAA,QACX,8BAAW,QAAA,QACX,4BAAW,QAAA,QACX,4BAAW,QAAA,QACX,4BAAW,QAAA,QACX,iCAAW,QAAA,QACX,oCAAW,QAAA,QACX,iCAAW,QAAA,QACX,+BAAW,QAAA,QACX,+BAAW,QAAA,QACX,iCAAW,QAAA,QACX,qBAAW,QAAA,QACX,4BAAW,QAAA,QACX,4BAAW,QAAA,QACX,2BAAW,QAAA,QACX,uBAAW,QAAA,QASX,wBAAW,QAAA,QACX,wBAAW,QAAA,QACX,4BAAW,QAAA,QACX,uBAAW,QAAA,QACX,wBAAW,QAAA,QACX,uBAAW,QAAA,QACX,yBAAW,QAAA,QACX,yBAAW,QAAA,QACX,+BAAW,QAAA,QACX,uBAAW,QAAA,QACX,6BAAW,QAAA,QACX,sBAAW,QAAA,QACX,wBAAW,QAAA,QACX,wBAAW,QAAA,QACX,4BAAW,QAAA,QACX,uBAAW,QAAA,QACX,4BAAW,QAAA,QACX,6BAAW,QAAA,QACX,2BAAW,QAAA,QACX,0BAAW,QAAA,QACX,sBAAW,QAAA,QACX,sBAAW,QAAA,QACX,sBAAW,QAAA,QACX,sBAAW,QAAA,QACX,wBAAW,QAAA,QACX,sBAAW,QAAA,QACX,wBAAW,QAAA,QACX,4BAAW,QAAA,QACX,mCAAW,QAAA,QACX,4BAAW,QAAA,QACX,oCAAW,QAAA,QACX,kCAAW,QAAA,QACX,iCAAW,QAAA,QACX,+BAAW,QAAA,QACX,sBAAW,QAAA,QACX,wBAAW,QAAA,QACX,6BAAW,QAAA,QACX,4BAAW,QAAA,QACX,6BAAW,QAAA,QACX,kCAAW,QAAA,QACX,mCAAW,QAAA,QACX,sCAAW,QAAA,QACX,0CAAW,QAAA,QACX,oCAAW,QAAA,QACX,wCAAW,QAAA,QACX,qCAAW,QAAA,QACX,iCAAW,QAAA,QACX,gCAAW,QAAA,QACX,kCAAW,QAAA,QACX,+BAAW,QAAA,QACX,0BAAW,QAAA,QACX,8BAAW,QAAA,QACX,4BAAW,QAAA,QACX,4BAAW,QAAA,QACX,6BAAW,QAAA,QACX,4BAAW,QAAA,QCtS/C,0BCgEE,QAAA,QHi+BF,EDNC,mBAAA,WGxhCI,gBAAiB,WFiiCZ,WAAY,WGl+BZ,OADL,QJg+BJ,mBAAA,WGthCI,gBAAiB,WACpB,WAAA,WHyhCD,KGrhCC,UAAW,KAEX,4BAAA,cAEA,KACA,YAAA,iBAAA,UAAA,MAAA,WHuhCD,UAAA,KGnhCC,YAAa,WF4hCb,MAAO,KACP,iBAAkB,KExhClB,OADA,MAEA,OHqhCD,SG/gCC,YAAa,QACb,UAAA,QACA,YAAA,QAEA,EFwhCA,MAAO,QEthCL,gBAAA,KAIF,QH8gCD,QKjkCC,MAAA,QACA,gBAAA,UF6DF,QACE,QAAA,IAAA,KAAA,yBHygCD,eAAA,KGlgCC,OHqgCD,OAAA,ECSD,IACE,eAAgB,ODDjB,4BM/kCC,0BLklCF,gBKnlCE,iBADA,eH4EA,QAAS,MACT,UAAA,KHugCD,OAAA,KGhgCC,aACA,cAAA,IAEA,eACA,QAAA,aC6FA,UAAA,KACK,OAAA,KACG,QAAA,IEvLR,YAAA,WACA,iBAAA,KACA,OAAA,IAAA,MAAA,KN+lCD,cAAA,IGjgCC,mBAAoB,IAAI,IAAI,YAC5B,cAAA,IAAA,IAAA,YHmgCD,WAAA,IAAA,IAAA,YG5/BC,YACA,cAAA,IAEA,GH+/BD,WAAA,KGv/BC,cAAe,KACf,OAAA,EACA,WAAA,IAAA,MAAA,KAEA,SACA,SAAA,SACA,MAAA,IACA,OAAA,IACA,QAAA,EHy/BD,OAAA,KGj/BC,SAAA,OF0/BA,KAAM,cEx/BJ,OAAA,EAEA,0BACA,yBACA,SAAA,OACA,MAAA,KHm/BH,OAAA,KGx+BC,OAAQ,EACR,SAAA,QH0+BD,KAAA,KCSD,cACE,OAAQ,QAQV,IACA,IMlpCE,IACA,IACA,IACA,INwoCF,GACA,GACA,GACA,GACA,GACA,GDAC,YAAA,QOlpCC,YAAa,IN2pCb,YAAa,IACb,MAAO,QAoBT,WAZA,UAaA,WAZA,UM5pCI,WN6pCJ,UM5pCI,WN6pCJ,UM5pCI,WN6pCJ,UDMC,WCLD,UACA,UAZA,SAaA,UAZA,SAaA,UAZA,SAaA,UAZA,SAaA,UAZA,SAaA,UAZA,SMppCE,YAAa,INwqCb,YAAa,EACb,MAAO,KAGT,IMxqCE,IAJF,IN2qCA,GAEA,GDLC,GCSC,WAAY,KACZ,cAAe,KASjB,WANA,UDCC,WCCD,UM5qCA,WN8qCA,UACA,UANA,SM5qCI,UN8qCJ,SM3qCA,UN6qCA,SAQE,UAAW,IAGb,IMprCE,IAJF,INurCA,GAEA,GDLC,GCSC,WAAY,KACZ,cAAe,KASjB,WANA,UDCC,WCCD,UMvrCA,WNyrCA,UACA,UANA,SMxrCI,UN0rCJ,SMtrCA,UNwrCA,SMxrCU,UAAA,IACV,IAAA,GAAU,UAAA,KACV,IAAA,GAAU,UAAA,KACV,IAAA,GAAU,UAAA,KACV,IAAA,GAAU,UAAA,KACV,IAAA,GAAU,UAAA,KAOR,IADF,GPssCC,UAAA,KCSD,EMzsCE,OAAA,EAAA,EAAA,KAEA,MPosCD,cAAA,KO/rCC,UAAW,KAwOX,YAAa,IA1OX,YAAA,IPssCH,yBO7rCC,MNssCE,UAAW,MMjsCf,OAAA,MAEE,UAAA,IAKF,MP0rCC,KO1rCsB,QAAA,KP6rCtB,iBAAA,QO5rCsB,WP+rCtB,WAAA,KO9rCsB,YPisCtB,WAAA,MOhsCsB,aPmsCtB,WAAA,OOlsCsB,cPqsCtB,WAAA,QOlsCsB,aPqsCtB,YAAA,OOpsCsB,gBPusCtB,eAAA,UOtsCsB,gBPysCtB,eAAA,UOrsCC,iBPwsCD,eAAA,WQ3yCC,YR8yCD,MAAA,KCSD,cOpzCI,MAAA,QAHF,qBDwGF,qBP6sCC,MAAA,QCSD,cO3zCI,MAAA,QAHF,qBD2GF,qBPitCC,MAAA,QCSD,WOl0CI,MAAA,QAHF,kBD8GF,kBPqtCC,MAAA,QCSD,cOz0CI,MAAA,QAHF,qBDiHF,qBPytCC,MAAA,QCSD,aOh1CI,MAAA,QDwHF,oBAHF,oBExHE,MAAA,QACA,YR01CA,MAAO,KQx1CL,iBAAA,QAHF,mBF8HF,mBP2tCC,iBAAA,QCSD,YQ/1CI,iBAAA,QAHF,mBFiIF,mBP+tCC,iBAAA,QCSD,SQt2CI,iBAAA,QAHF,gBFoIF,gBPmuCC,iBAAA,QCSD,YQ72CI,iBAAA,QAHF,mBFuIF,mBPuuCC,iBAAA,QCSD,WQp3CI,iBAAA,QF6IF,kBADF,kBAEE,iBAAA,QPsuCD,aO7tCC,eAAgB,INsuChB,OAAQ,KAAK,EAAE,KMpuCf,cAAA,IAAA,MAAA,KAFF,GPkuCC,GCSC,WAAY,EACZ,cAAe,KM9tCf,MP0tCD,MO3tCD,MAPI,MASF,cAAA,EAIF,eALE,aAAA,EACA,WAAA,KPkuCD,aO9tCC,aAAc,EAKZ,YAAA,KACA,WAAA,KP6tCH,gBOvtCC,QAAS,aACT,cAAA,IACA,aAAA,IAEF,GNguCE,WAAY,EM9tCZ,cAAA,KAGA,GADF,GP0tCC,YAAA,WOttCC,GPytCD,YAAA,IOnnCD,GAvFM,YAAA,EAEA,yBACA,kBGtNJ,MAAA,KACA,MAAA,MACA,SAAA,OVq6CC,MAAA,KO7nCC,WAAY,MAhFV,cAAA,SPgtCH,YAAA,OOtsCD,kBNgtCE,YAAa,OM1sCjB,0BPssCC,YOrsCC,OAAA,KA9IqB,cAAA,IAAA,OAAA,KAmJvB,YACE,UAAA,IACA,eAAA,UAEA,WPssCD,QAAA,KAAA,KOjsCG,OAAA,EAAA,EAAA,KN0sCF,UAAW,OACX,YAAa,IAAI,MAAM,KMptCzB,yBP+sCC,wBO/sCD,yBNytCE,cAAe,EMnsCb,kBAFA,kBACA,iBPksCH,QAAA,MO/rCG,UAAA,INwsCF,YAAa,WACb,MAAO,KMhsCT,yBP2rCC,yBO3rCD,wBAEE,QAAA,cAEA,oBACA,sBACA,cAAA,KP6rCD,aAAA,EOvrCG,WAAA,MNgsCF,aAAc,IAAI,MAAM,KACxB,YAAa,EMhsCX,kCNksCJ,kCMnsCe,iCACX,oCNmsCJ,oCDLC,mCCUC,QAAS,GMjsCX,iCNmsCA,iCMzsCM,gCAOJ,mCNmsCF,mCDLC,kCO7rCC,QAAA,cPksCD,QWv+CC,cAAe,KVg/Cf,WAAY,OACZ,YAAa,WU7+Cb,KXy+CD,IWr+CD,IACE,KACA,YAAA,MAAA,OAAA,SAAA,cAAA,UAEA,KACA,QAAA,IAAA,IXu+CD,UAAA,IWn+CC,MAAO,QACP,iBAAA,QACA,cAAA,IAEA,IACA,QAAA,IAAA,IACA,UAAA,IV4+CA,MU5+CA,KXq+CD,iBAAA,KW3+CC,cAAe,IASb,mBAAA,MAAA,EAAA,KAAA,EAAA,gBACA,WAAA,MAAA,EAAA,KAAA,EAAA,gBAEA,QV6+CF,QU7+CE,EXq+CH,UAAA,KWh+CC,YAAa,IACb,mBAAA,KACA,WAAA,KAEA,IACA,QAAA,MACA,QAAA,MACA,OAAA,EAAA,EAAA,KACA,UAAA,KACA,YAAA,WACA,MAAA,KACA,WAAA,UXk+CD,UAAA,WW7+CC,iBAAkB,QAehB,OAAA,IAAA,MAAA,KACA,cAAA,IAEA,SACA,QAAA,EACA,UAAA,QXi+CH,MAAA,QW59CC,YAAa,SACb,iBAAA,YACA,cAAA,EC1DF,gBCHE,WAAA,MACA,WAAA,OAEA,Wb8hDD,cAAA,KYxhDC,aAAA,KAqEA,aAAc,KAvEZ,YAAA,KZ+hDH,yBY1hDC,WAkEE,MAAO,OZ69CV,yBY5hDC,WA+DE,MAAO,OZk+CV,0BYzhDC,WCvBA,MAAA,QAGA,iBbmjDD,cAAA,KYthDC,aAAc,KCvBd,aAAA,KACA,YAAA,KCAE,KACE,aAAA,MAEA,YAAA,MAGA,UAAA,WAAA,WAAA,WAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,WAAA,WAAA,WAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,WAAA,WAAA,WAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,WAAA,WAAA,WAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UdgjDL,SAAA,SchiDG,WAAA,IACE,cAAA,KdkiDL,aAAA,Kc1hDG,UAAA,WAAA,WAAA,WAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,Ud6hDH,MAAA,Kc7hDG,WdgiDH,MAAA,KchiDG,WdmiDH,MAAA,acniDG,WdsiDH,MAAA,actiDG,UdyiDH,MAAA,IcziDG,Ud4iDH,MAAA,ac5iDG,Ud+iDH,MAAA,ac/iDG,UdkjDH,MAAA,IcljDG,UdqjDH,MAAA,acrjDG,UdwjDH,MAAA,acxjDG,Ud2jDH,MAAA,Ic3jDG,Ud8jDH,MAAA,ac/iDG,UdkjDH,MAAA,YcljDG,gBdqjDH,MAAA,KcrjDG,gBdwjDH,MAAA,acxjDG,gBd2jDH,MAAA,ac3jDG,ed8jDH,MAAA,Ic9jDG,edikDH,MAAA,acjkDG,edokDH,MAAA,acpkDG,edukDH,MAAA,IcvkDG,ed0kDH,MAAA,ac1kDG,ed6kDH,MAAA,ac7kDG,edglDH,MAAA,IchlDG,edmlDH,MAAA,ac9kDG,edilDH,MAAA,YchmDG,edmmDH,MAAA,KcnmDG,gBdsmDH,KAAA,KctmDG,gBdymDH,KAAA,aczmDG,gBd4mDH,KAAA,ac5mDG,ed+mDH,KAAA,Ic/mDG,edknDH,KAAA,aclnDG,edqnDH,KAAA,acrnDG,edwnDH,KAAA,IcxnDG,ed2nDH,KAAA,ac3nDG,ed8nDH,KAAA,ac9nDG,edioDH,KAAA,IcjoDG,edooDH,KAAA,ac/nDG,edkoDH,KAAA,YcnnDG,edsnDH,KAAA,KctnDG,kBdynDH,YAAA,KcznDG,kBd4nDH,YAAA,ac5nDG,kBd+nDH,YAAA,ac/nDG,iBdkoDH,YAAA,IcloDG,iBdqoDH,YAAA,acroDG,iBdwoDH,YAAA,acxoDG,iBd2oDH,YAAA,Ic3oDG,iBd8oDH,YAAA,ac9oDG,iBdipDH,YAAA,acjpDG,iBdopDH,YAAA,IcppDG,iBdupDH,YAAA,acvpDG,iBd0pDH,YAAA,Yc5rDG,iBACE,YAAA,EAOJ,yBACE,UAAA,WAAA,WAAA,WAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,Ud0rDD,MAAA,Kc1rDC,Wd6rDD,MAAA,Kc7rDC,WdgsDD,MAAA,achsDC,WdmsDD,MAAA,acnsDC,UdssDD,MAAA,IctsDC,UdysDD,MAAA,aczsDC,Ud4sDD,MAAA,ac5sDC,Ud+sDD,MAAA,Ic/sDC,UdktDD,MAAA,acltDC,UdqtDD,MAAA,acrtDC,UdwtDD,MAAA,IcxtDC,Ud2tDD,MAAA,ac5sDC,Ud+sDD,MAAA,Yc/sDC,gBdktDD,MAAA,KcltDC,gBdqtDD,MAAA,acrtDC,gBdwtDD,MAAA,acxtDC,ed2tDD,MAAA,Ic3tDC,ed8tDD,MAAA,ac9tDC,ediuDD,MAAA,acjuDC,edouDD,MAAA,IcpuDC,eduuDD,MAAA,acvuDC,ed0uDD,MAAA,ac1uDC,ed6uDD,MAAA,Ic7uDC,edgvDD,MAAA,ac3uDC,ed8uDD,MAAA,Yc7vDC,edgwDD,MAAA,KchwDC,gBdmwDD,KAAA,KcnwDC,gBdswDD,KAAA,actwDC,gBdywDD,KAAA,aczwDC,ed4wDD,KAAA,Ic5wDC,ed+wDD,KAAA,ac/wDC,edkxDD,KAAA,aclxDC,edqxDD,KAAA,IcrxDC,edwxDD,KAAA,acxxDC,ed2xDD,KAAA,ac3xDC,ed8xDD,KAAA,Ic9xDC,ediyDD,KAAA,ac5xDC,ed+xDD,KAAA,YchxDC,edmxDD,KAAA,KcnxDC,kBdsxDD,YAAA,KctxDC,kBdyxDD,YAAA,aczxDC,kBd4xDD,YAAA,ac5xDC,iBd+xDD,YAAA,Ic/xDC,iBdkyDD,YAAA,aclyDC,iBdqyDD,YAAA,acryDC,iBdwyDD,YAAA,IcxyDC,iBd2yDD,YAAA,ac3yDC,iBd8yDD,YAAA,ac9yDC,iBdizDD,YAAA,IcjzDC,iBdozDD,YAAA,acpzDC,iBduzDD,YAAA,YY9yDD,iBE3CE,YAAA,GAQF,yBACE,UAAA,WAAA,WAAA,WAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,Udw1DD,MAAA,Kcx1DC,Wd21DD,MAAA,Kc31DC,Wd81DD,MAAA,ac91DC,Wdi2DD,MAAA,acj2DC,Udo2DD,MAAA,Icp2DC,Udu2DD,MAAA,acv2DC,Ud02DD,MAAA,ac12DC,Ud62DD,MAAA,Ic72DC,Udg3DD,MAAA,ach3DC,Udm3DD,MAAA,acn3DC,Uds3DD,MAAA,Ict3DC,Udy3DD,MAAA,ac12DC,Ud62DD,MAAA,Yc72DC,gBdg3DD,MAAA,Kch3DC,gBdm3DD,MAAA,acn3DC,gBds3DD,MAAA,act3DC,edy3DD,MAAA,Icz3DC,ed43DD,MAAA,ac53DC,ed+3DD,MAAA,ac/3DC,edk4DD,MAAA,Icl4DC,edq4DD,MAAA,acr4DC,edw4DD,MAAA,acx4DC,ed24DD,MAAA,Ic34DC,ed84DD,MAAA,acz4DC,ed44DD,MAAA,Yc35DC,ed85DD,MAAA,Kc95DC,gBdi6DD,KAAA,Kcj6DC,gBdo6DD,KAAA,acp6DC,gBdu6DD,KAAA,acv6DC,ed06DD,KAAA,Ic16DC,ed66DD,KAAA,ac76DC,edg7DD,KAAA,ach7DC,edm7DD,KAAA,Icn7DC,eds7DD,KAAA,act7DC,edy7DD,KAAA,acz7DC,ed47DD,KAAA,Ic57DC,ed+7DD,KAAA,ac17DC,ed67DD,KAAA,Yc96DC,edi7DD,KAAA,Kcj7DC,kBdo7DD,YAAA,Kcp7DC,kBdu7DD,YAAA,acv7DC,kBd07DD,YAAA,ac17DC,iBd67DD,YAAA,Ic77DC,iBdg8DD,YAAA,ach8DC,iBdm8DD,YAAA,acn8DC,iBds8DD,YAAA,Ict8DC,iBdy8DD,YAAA,acz8DC,iBd48DD,YAAA,ac58DC,iBd+8DD,YAAA,Ic/8DC,iBdk9DD,YAAA,acl9DC,iBdq9DD,YAAA,YYz8DD,iBE9CE,YAAA,GAQF,0BACE,UAAA,WAAA,WAAA,WAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,Uds/DD,MAAA,Kct/DC,Wdy/DD,MAAA,Kcz/DC,Wd4/DD,MAAA,ac5/DC,Wd+/DD,MAAA,ac//DC,UdkgED,MAAA,IclgEC,UdqgED,MAAA,acrgEC,UdwgED,MAAA,acxgEC,Ud2gED,MAAA,Ic3gEC,Ud8gED,MAAA,ac9gEC,UdihED,MAAA,acjhEC,UdohED,MAAA,IcphEC,UduhED,MAAA,acxgEC,Ud2gED,MAAA,Yc3gEC,gBd8gED,MAAA,Kc9gEC,gBdihED,MAAA,acjhEC,gBdohED,MAAA,acphEC,eduhED,MAAA,IcvhEC,ed0hED,MAAA,ac1hEC,ed6hED,MAAA,ac7hEC,edgiED,MAAA,IchiEC,edmiED,MAAA,acniEC,edsiED,MAAA,actiEC,edyiED,MAAA,IcziEC,ed4iED,MAAA,acviEC,ed0iED,MAAA,YczjEC,ed4jED,MAAA,Kc5jEC,gBd+jED,KAAA,Kc/jEC,gBdkkED,KAAA,aclkEC,gBdqkED,KAAA,acrkEC,edwkED,KAAA,IcxkEC,ed2kED,KAAA,ac3kEC,ed8kED,KAAA,ac9kEC,edilED,KAAA,IcjlEC,edolED,KAAA,acplEC,edulED,KAAA,acvlEC,ed0lED,KAAA,Ic1lEC,ed6lED,KAAA,acxlEC,ed2lED,KAAA,Yc5kEC,ed+kED,KAAA,Kc/kEC,kBdklED,YAAA,KcllEC,kBdqlED,YAAA,acrlEC,kBdwlED,YAAA,acxlEC,iBd2lED,YAAA,Ic3lEC,iBd8lED,YAAA,ac9lEC,iBdimED,YAAA,acjmEC,iBdomED,YAAA,IcpmEC,iBdumED,YAAA,acvmEC,iBd0mED,YAAA,ac1mEC,iBd6mED,YAAA,Ic7mEC,iBdgnED,YAAA,achnEC,iBdmnED,YAAA,YetrED,iBACA,YAAA,GAGA,MACA,iBAAA,YAEA,QfyrED,YAAA,IevrEC,eAAgB,IAChB,MAAA,KfyrED,WAAA,KelrEC,GACA,WAAA,KfsrED,OexrEC,MAAO,KdmsEP,UAAW,KACX,cAAe,KcvrET,mBd0rER,mBczrEQ,mBAHA,mBACA,mBd0rER,mBDHC,QAAA,IensEC,YAAa,WAoBX,eAAA,IACA,WAAA,IAAA,MAAA,KArBJ,mBdktEE,eAAgB,OAChB,cAAe,IAAI,MAAM,KDJ1B,uCCMD,uCcrtEA,wCdstEA,wCclrEI,2CANI,2CforEP,WAAA,EezqEG,mBf4qEH,WAAA,IAAA,MAAA,KCWD,cACE,iBAAkB,Kc/pEpB,6BdkqEA,6BcjqEE,6BAZM,6BfsqEP,6BCMD,6BDHC,QAAA,ICWD,gBACE,OAAQ,IAAI,MAAM,Kc1qEpB,4Bd6qEA,4Bc7qEA,4BAQQ,4Bf8pEP,4BCMD,4Bc7pEM,OAAA,IAAA,MAAA,KAYF,4BAFJ,4BfopEC,oBAAA,IevoEG,yCf0oEH,iBAAA,QehoEC,4BACA,iBAAA,QfooED,uBe9nEG,SAAA,OdyoEF,QAAS,acxoEL,MAAA,KAEA,sBfioEL,sBgB7wEC,SAAA,OfwxEA,QAAS,WACT,MAAO,KAST,0BerxEE,0Bf+wEF,0BAGA,0BexxEM,0BAMJ,0BfgxEF,0BAGA,0BACA,0BDNC,0BCAD,0BAGA,0BASE,iBAAkB,QDLnB,sCgBlyEC,sCAAA,oCfyyEF,sCetxEM,sCf2xEJ,iBAAkB,QASpB,2Be1yEE,2BfoyEF,2BAGA,2Be7yEM,2BAMJ,2BfqyEF,2BAGA,2BACA,2BDNC,2BCAD,2BAGA,2BASE,iBAAkB,QDLnB,uCgBvzEC,uCAAA,qCf8zEF,uCe3yEM,uCfgzEJ,iBAAkB,QASpB,wBe/zEE,wBfyzEF,wBAGA,wBel0EM,wBAMJ,wBf0zEF,wBAGA,wBACA,wBDNC,wBCAD,wBAGA,wBASE,iBAAkB,QDLnB,oCgB50EC,oCAAA,kCfm1EF,oCeh0EM,oCfq0EJ,iBAAkB,QASpB,2Bep1EE,2Bf80EF,2BAGA,2Bev1EM,2BAMJ,2Bf+0EF,2BAGA,2BACA,2BDNC,2BCAD,2BAGA,2BASE,iBAAkB,QDLnB,uCgBj2EC,uCAAA,qCfw2EF,uCer1EM,uCf01EJ,iBAAkB,QASpB,0Bez2EE,0Bfm2EF,0BAGA,0Be52EM,0BAMJ,0Bfo2EF,0BAGA,0BACA,0BDNC,0BCAD,0BAGA,0BASE,iBAAkB,QDLnB,sCehtEC,sCADF,oCdwtEA,sCe12EM,sCDoJJ,iBAAA,QA6DF,kBACE,WAAY,KA3DV,WAAA,KAEA,oCACA,kBACA,MAAA,KfotED,cAAA,Ke7pEC,WAAY,OAnDV,mBAAA,yBfmtEH,OAAA,IAAA,MAAA,KCWD,yBACE,cAAe,Ec5qEjB,qCd+qEA,qCcjtEI,qCARM,qCfktET,qCCMD,qCDHC,YAAA,OCWD,kCACE,OAAQ,EcvrEV,0Dd0rEA,0Dc1rEA,0DAzBU,0Df4sET,0DCMD,0DAME,YAAa,Ec/rEf,yDdksEA,yDclsEA,yDArBU,yDfgtET,yDCMD,yDAME,aAAc,EDLjB,yDe1sEW,yDEzNV,yDjBk6EC,yDiBj6ED,cAAA,GAMA,SjBk6ED,UAAA,EiB/5EC,QAAS,EACT,OAAA,EACA,OAAA,EAEA,OACA,QAAA,MACA,MAAA,KACA,QAAA,EACA,cAAA,KACA,UAAA,KjBi6ED,YAAA,QiB95EC,MAAO,KACP,OAAA,EACA,cAAA,IAAA,MAAA,QAEA,MjBg6ED,QAAA,aiBr5EC,UAAW,Kb4BX,cAAA,IACG,YAAA,IJ63EJ,mBiBr5EC,mBAAoB,WhBg6EjB,gBAAiB,WgB95EpB,WAAA,WjBy5ED,qBiBv5EC,kBAGA,OAAQ,IAAI,EAAE,EACd,WAAA,MjBs5ED,YAAA,OiBj5EC,iBACA,QAAA,MAIF,kBhB25EE,QAAS,MgBz5ET,MAAA,KAIF,iBAAA,ahB05EE,OAAQ,KI99ER,uBY2EF,2BjB64EC,wBiB54EC,QAAA,IAAA,KAAA,yBACA,eAAA,KAEA,OACA,QAAA,MjB+4ED,YAAA,IiBr3EC,UAAW,KACX,YAAA,WACA,MAAA,KAEA,cACA,QAAA,MACA,MAAA,KACA,OAAA,KACA,QAAA,IAAA,KACA,UAAA,KACA,YAAA,WACA,MAAA,KbxDA,iBAAA,KACQ,iBAAA,KAyHR,OAAA,IAAA,MAAA,KACK,cAAA,IACG,mBAAA,MAAA,EAAA,IAAA,IAAA,iBJwzET,WAAA,MAAA,EAAA,IAAA,IAAA,iBkBh8EC,mBAAA,aAAA,YAAA,KAAA,mBAAA,YAAA,KACE,cAAA,aAAA,YAAA,KAAA,WAAA,YAAA,KACA,WAAA,aAAA,YAAA,KAAA,WAAA,YAAA,KdWM,oBJy7ET,aAAA,QIx5EC,QAAA,EACE,mBAAA,MAAA,EAAA,IAAA,IAAA,iBAAA,EAAA,EAAA,IAAA,qBACA,WAAA,MAAA,EAAA,IAAA,IAAA,iBAAA,EAAA,EAAA,IAAA,qBAEF,gCAA0B,MAAA,KJ25E3B,QAAA,EI15EiC,oCJ65EjC,MAAA,KiBh4EG,yCACA,MAAA,KAQF,0BhBs4EA,iBAAkB,YAClB,OAAQ,EgBn4EN,wBjB63EH,wBiB13EC,iChBq4EA,iBAAkB,KgBn4EhB,QAAA,EAIF,wBACE,iCjB03EH,OAAA,YiB72EC,sBjBg3ED,OAAA,KiB91EG,mBhB02EF,mBAAoB,KAEtB,qDgB32EM,8BjBo2EH,8BiBj2EC,wCAAA,+BhB62EA,YAAa,KgB32EX,iCjBy2EH,iCiBt2EC,2CAAA,kChB02EF,0BACA,0BACA,oCACA,2BAKE,YAAa,KgBh3EX,iCjB82EH,iCACF,2CiBp2EC,kChBu2EA,0BACA,0BACA,oCACA,2BgBz2EA,YAAA,MhBi3EF,YgBv2EE,cAAA,KAGA,UADA,OjBi2ED,SAAA,SiBr2EC,QAAS,MhBg3ET,WAAY,KgBx2EV,cAAA,KAGA,gBADA,aAEA,WAAA,KjBi2EH,aAAA,KiB91EC,cAAe,EhBy2Ef,YAAa,IACb,OAAQ,QgBp2ER,+BjBg2ED,sCiBl2EC,yBACA,gCAIA,SAAU,ShBw2EV,WAAY,MgBt2EZ,YAAA,MAIF,oBAAA,cAEE,WAAA,KAGA,iBADA,cAEA,SAAA,SACA,QAAA,aACA,aAAA,KjB61ED,cAAA,EiB31EC,YAAa,IhBs2Eb,eAAgB,OgBp2EhB,OAAA,QAUA,kCjBo1ED,4BCWC,WAAY,EACZ,YAAa,KgBv1Eb,wCAAA,qCjBm1ED,8BCOD,+BgBh2EI,2BhB+1EJ,4BAME,OAAQ,YDNT,0BiBv1EG,uBAMF,oCAAA,iChB61EA,OAAQ,YDNT,yBiBp1EK,sBAaJ,mCAFF,gCAGE,OAAA,YAGA,qBjBy0ED,WAAA,KiBv0EC,YAAA,IhBk1EA,eAAgB,IgBh1Ed,cAAA,EjB00EH,8BiB5zED,8BCnQE,cAAA,EACA,aAAA,EAEA,UACA,OAAA,KlBkkFD,QAAA,IAAA,KkBhkFC,UAAA,KACE,YAAA,IACA,cAAA,IAGF,gBjB0kFA,OAAQ,KiBxkFN,YAAA,KD2PA,0BAFJ,kBAGI,OAAA,KAEA,6BACA,OAAA,KjBy0EH,QAAA,IAAA,KiB/0EC,UAAW,KAST,YAAA,IACA,cAAA,IAVJ,mChB81EE,OAAQ,KgBh1EN,YAAA,KAGA,6CAjBJ,qCAkBI,OAAA,KAEA,oCACA,OAAA,KjBy0EH,WAAA,KiBr0EC,QAAS,IAAI,KC/Rb,UAAA,KACA,YAAA,IAEA,UACA,OAAA,KlBumFD,QAAA,KAAA,KkBrmFC,UAAA,KACE,YAAA,UACA,cAAA,IAGF,gBjB+mFA,OAAQ,KiB7mFN,YAAA,KDuRA,0BAFJ,kBAGI,OAAA,KAEA,6BACA,OAAA,KjBk1EH,QAAA,KAAA,KiBx1EC,UAAW,KAST,YAAA,UACA,cAAA,IAVJ,mChBu2EE,OAAQ,KgBz1EN,YAAA,KAGA,6CAjBJ,qCAkBI,OAAA,KAEA,oCACA,OAAA,KjBk1EH,WAAA,KiBz0EC,QAAS,KAAK,KAEd,UAAA,KjB00ED,YAAA,UiBt0EG,cjBy0EH,SAAA,SiBp0EC,4BACA,cAAA,OAEA,uBACA,SAAA,SACA,IAAA,EACA,MAAA,EACA,QAAA,EACA,QAAA,MACA,MAAA,KjBu0ED,OAAA,KiBr0EC,YAAa,KhBg1Eb,WAAY,OACZ,eAAgB,KDLjB,oDiBv0EC,uCADA,iCAGA,MAAO,KhBg1EP,OAAQ,KACR,YAAa,KDLd,oDiBv0EC,uCADA,iCAKA,MAAO,KhB80EP,OAAQ,KACR,YAAa,KAKf,uBAEA,8BAJA,4BADA,yBAEA,oBAEA,2BDNC,4BkBruFG,mCAJA,yBD0ZJ,gCbvWE,MAAA,QJ2rFD,2BkBxuFG,aAAA,QACE,mBAAA,MAAA,EAAA,IAAA,IAAA,iBd4CJ,WAAA,MAAA,EAAA,IAAA,IAAA,iBJgsFD,iCiBz1EC,aAAc,QC5YZ,mBAAA,MAAA,EAAA,IAAA,IAAA,iBAAA,EAAA,EAAA,IAAA,QACA,WAAA,MAAA,EAAA,IAAA,IAAA,iBAAA,EAAA,EAAA,IAAA,QlByuFH,gCiB91EC,MAAO,QCtYL,iBAAA,QlBuuFH,aAAA,QCWD,oCACE,MAAO,QAKT,uBAEA,8BAJA,4BADA,yBAEA,oBAEA,2BDNC,4BkBnwFG,mCAJA,yBD6ZJ,gCb1WE,MAAA,QJytFD,2BkBtwFG,aAAA,QACE,mBAAA,MAAA,EAAA,IAAA,IAAA,iBd4CJ,WAAA,MAAA,EAAA,IAAA,IAAA,iBJ8tFD,iCiBp3EC,aAAc,QC/YZ,mBAAA,MAAA,EAAA,IAAA,IAAA,iBAAA,EAAA,EAAA,IAAA,QACA,WAAA,MAAA,EAAA,IAAA,IAAA,iBAAA,EAAA,EAAA,IAAA,QlBuwFH,gCiBz3EC,MAAO,QCzYL,iBAAA,QlBqwFH,aAAA,QCWD,oCACE,MAAO,QAKT,qBAEA,4BAJA,0BADA,uBAEA,kBAEA,yBDNC,0BkBjyFG,iCAJA,uBDgaJ,8Bb7WE,MAAA,QJuvFD,yBkBpyFG,aAAA,QACE,mBAAA,MAAA,EAAA,IAAA,IAAA,iBd4CJ,WAAA,MAAA,EAAA,IAAA,IAAA,iBJ4vFD,+BiB/4EC,aAAc,QClZZ,mBAAA,MAAA,EAAA,IAAA,IAAA,iBAAA,EAAA,EAAA,IAAA,QACA,WAAA,MAAA,EAAA,IAAA,IAAA,iBAAA,EAAA,EAAA,IAAA,QlBqyFH,8BiBp5EC,MAAO,QC5YL,iBAAA,QlBmyFH,aAAA,QiB/4EG,kCjBk5EH,MAAA,QiB/4EG,2CjBk5EH,IAAA,KiBv4EC,mDACA,IAAA,EAEA,YjB04ED,QAAA,MiBvzEC,WAAY,IAwEZ,cAAe,KAtIX,MAAA,QAEA,yBjBy3EH,yBiBrvEC,QAAS,aA/HP,cAAA,EACA,eAAA,OjBw3EH,2BiB1vEC,QAAS,aAxHP,MAAA,KjBq3EH,eAAA,OiBj3EG,kCACA,QAAA,aAmHJ,0BhB4wEE,QAAS,aACT,eAAgB,OgBr3Ed,wCjB82EH,6CiBtwED,2CjBywEC,MAAA,KiB72EG,wCACA,MAAA,KAmGJ,4BhBwxEE,cAAe,EgBp3Eb,eAAA,OAGA,uBADA,oBjB82EH,QAAA,aiBpxEC,WAAY,EhB+xEZ,cAAe,EgBr3EX,eAAA,OAsFN,6BAAA,0BAjFI,aAAA,EAiFJ,4CjB6xEC,sCiBx2EG,SAAA,SjB22EH,YAAA,EiBh2ED,kDhB42EE,IAAK,GgBl2EL,2BjB+1EH,kCiBh2EG,wBAEA,+BAXF,YAAa,IhBo3Eb,WAAY,EgBn2EV,cAAA,EJviBF,2BIshBF,wBJrhBE,WAAA,KI4jBA,6BAyBA,aAAc,MAnCV,YAAA,MAEA,yBjBw1EH,gCACF,YAAA,IiBx3EG,cAAe,EAwCf,WAAA,OAwBJ,sDAdQ,MAAA,KjB80EL,yBACF,+CiBn0EC,YAAA,KAEE,UAAW,MjBs0EZ,yBACF,+CmBp6FG,YAAa,IACf,UAAA,MAGA,KACA,QAAA,aACA,QAAA,IAAA,KAAA,cAAA,EACA,UAAA,KACA,YAAA,IACA,YAAA,WACA,WAAA,OC0CA,YAAA,OACA,eAAA,OACA,iBAAA,aACA,aAAA,ahB+JA,OAAA,QACG,oBAAA,KACC,iBAAA,KACI,gBAAA,KJ+tFT,YAAA,KmBv6FG,iBAAA,KlBm7FF,OAAQ,IAAI,MAAM,YAClB,cAAe,IkB96Ff,kBdzBA,kBACA,WLk8FD,kBCOD,kBADA,WAME,QAAS,IAAI,KAAK,yBAClB,eAAgB,KkBh7FhB,WnBy6FD,WmB56FG,WlBw7FF,MAAO,KkBn7FL,gBAAA,Kf6BM,YADR,YJk5FD,iBAAA,KmBz6FC,QAAA,ElBq7FA,mBAAoB,MAAM,EAAE,IAAI,IAAI,iBAC5B,WAAY,MAAM,EAAE,IAAI,IAAI,iBoBh+FpC,cAGA,ejB8DA,wBACQ,OAAA,YJ05FT,OAAA,kBmBz6FG,mBAAA,KlBq7FM,WAAY,KkBn7FhB,QAAA,IASN,eC3DE,yBACA,eAAA,KpBi+FD,aoB99FC,MAAA,KnB0+FA,iBAAkB,KmBx+FhB,aAAA,KpBk+FH,mBoBh+FO,mBAEN,MAAA,KACE,iBAAA,QACA,aAAA,QpBi+FH,mBoB99FC,MAAA,KnB0+FA,iBAAkB,QAClB,aAAc,QmBt+FR,oBADJ,oBpBi+FH,mCoB99FG,MAAA,KnB0+FF,iBAAkB,QAClB,aAAc,QmBt+FN,0BnB4+FV,0BAHA,0BmB1+FM,0BnB4+FN,0BAHA,0BDFC,yCoBx+FK,yCnB4+FN,yCmBv+FE,MAAA,KnB++FA,iBAAkB,QAClB,aAAc,QmBx+FZ,oBpBg+FH,oBoBh+FG,mCnB6+FF,iBAAkB,KmBz+FV,4BnB8+FV,4BAHA,4BDHC,6BCOD,6BAHA,6BkB39FA,sCClBM,sCnB8+FN,sCmBx+FI,iBAAA,KACA,aAAA,KDcJ,oBC9DE,MAAA,KACA,iBAAA,KpB0hGD,aoBvhGC,MAAA,KnBmiGA,iBAAkB,QmBjiGhB,aAAA,QpB2hGH,mBoBzhGO,mBAEN,MAAA,KACE,iBAAA,QACA,aAAA,QpB0hGH,mBoBvhGC,MAAA,KnBmiGA,iBAAkB,QAClB,aAAc,QmB/hGR,oBADJ,oBpB0hGH,mCoBvhGG,MAAA,KnBmiGF,iBAAkB,QAClB,aAAc,QmB/hGN,0BnBqiGV,0BAHA,0BmBniGM,0BnBqiGN,0BAHA,0BDFC,yCoBjiGK,yCnBqiGN,yCmBhiGE,MAAA,KnBwiGA,iBAAkB,QAClB,aAAc,QmBjiGZ,oBpByhGH,oBoBzhGG,mCnBsiGF,iBAAkB,KmBliGV,4BnBuiGV,4BAHA,4BDHC,6BCOD,6BAHA,6BkBjhGA,sCCrBM,sCnBuiGN,sCmBjiGI,iBAAA,QACA,aAAA,QDkBJ,oBClEE,MAAA,QACA,iBAAA,KpBmlGD,aoBhlGC,MAAA,KnB4lGA,iBAAkB,QmB1lGhB,aAAA,QpBolGH,mBoBllGO,mBAEN,MAAA,KACE,iBAAA,QACA,aAAA,QpBmlGH,mBoBhlGC,MAAA,KnB4lGA,iBAAkB,QAClB,aAAc,QmBxlGR,oBADJ,oBpBmlGH,mCoBhlGG,MAAA,KnB4lGF,iBAAkB,QAClB,aAAc,QmBxlGN,0BnB8lGV,0BAHA,0BmB5lGM,0BnB8lGN,0BAHA,0BDFC,yCoB1lGK,yCnB8lGN,yCmBzlGE,MAAA,KnBimGA,iBAAkB,QAClB,aAAc,QmB1lGZ,oBpBklGH,oBoBllGG,mCnB+lGF,iBAAkB,KmB3lGV,4BnBgmGV,4BAHA,4BDHC,6BCOD,6BAHA,6BkBtkGA,sCCzBM,sCnBgmGN,sCmB1lGI,iBAAA,QACA,aAAA,QDsBJ,oBCtEE,MAAA,QACA,iBAAA,KpB4oGD,UoBzoGC,MAAA,KnBqpGA,iBAAkB,QmBnpGhB,aAAA,QpB6oGH,gBoB3oGO,gBAEN,MAAA,KACE,iBAAA,QACA,aAAA,QpB4oGH,gBoBzoGC,MAAA,KnBqpGA,iBAAkB,QAClB,aAAc,QmBjpGR,iBADJ,iBpB4oGH,gCoBzoGG,MAAA,KnBqpGF,iBAAkB,QAClB,aAAc,QmBjpGN,uBnBupGV,uBAHA,uBmBrpGM,uBnBupGN,uBAHA,uBDFC,sCoBnpGK,sCnBupGN,sCmBlpGE,MAAA,KnB0pGA,iBAAkB,QAClB,aAAc,QmBnpGZ,iBpB2oGH,iBoB3oGG,gCnBwpGF,iBAAkB,KmBppGV,yBnBypGV,yBAHA,yBDHC,0BCOD,0BAHA,0BkB3nGA,mCC7BM,mCnBypGN,mCmBnpGI,iBAAA,QACA,aAAA,QD0BJ,iBC1EE,MAAA,QACA,iBAAA,KpBqsGD,aoBlsGC,MAAA,KnB8sGA,iBAAkB,QmB5sGhB,aAAA,QpBssGH,mBoBpsGO,mBAEN,MAAA,KACE,iBAAA,QACA,aAAA,QpBqsGH,mBoBlsGC,MAAA,KnB8sGA,iBAAkB,QAClB,aAAc,QmB1sGR,oBADJ,oBpBqsGH,mCoBlsGG,MAAA,KnB8sGF,iBAAkB,QAClB,aAAc,QmB1sGN,0BnBgtGV,0BAHA,0BmB9sGM,0BnBgtGN,0BAHA,0BDFC,yCoB5sGK,yCnBgtGN,yCmB3sGE,MAAA,KnBmtGA,iBAAkB,QAClB,aAAc,QmB5sGZ,oBpBosGH,oBoBpsGG,mCnBitGF,iBAAkB,KmB7sGV,4BnBktGV,4BAHA,4BDHC,6BCOD,6BAHA,6BkBhrGA,sCCjCM,sCnBktGN,sCmB5sGI,iBAAA,QACA,aAAA,QD8BJ,oBC9EE,MAAA,QACA,iBAAA,KpB8vGD,YoB3vGC,MAAA,KnBuwGA,iBAAkB,QmBrwGhB,aAAA,QpB+vGH,kBoB7vGO,kBAEN,MAAA,KACE,iBAAA,QACA,aAAA,QpB8vGH,kBoB3vGC,MAAA,KnBuwGA,iBAAkB,QAClB,aAAc,QmBnwGR,mBADJ,mBpB8vGH,kCoB3vGG,MAAA,KnBuwGF,iBAAkB,QAClB,aAAc,QmBnwGN,yBnBywGV,yBAHA,yBmBvwGM,yBnBywGN,yBAHA,yBDFC,wCoBrwGK,wCnBywGN,wCmBpwGE,MAAA,KnB4wGA,iBAAkB,QAClB,aAAc,QmBrwGZ,mBpB6vGH,mBoB7vGG,kCnB0wGF,iBAAkB,KmBtwGV,2BnB2wGV,2BAHA,2BDHC,4BCOD,4BAHA,4BkBruGA,qCCrCM,qCnB2wGN,qCmBrwGI,iBAAA,QACA,aAAA,QDuCJ,mBACE,MAAA,QACA,iBAAA,KnB+tGD,UmB5tGC,YAAA,IlBwuGA,MAAO,QACP,cAAe,EAEjB,UGzwGE,iBemCE,iBflCM,oBJkwGT,6BmB7tGC,iBAAA,YlByuGA,mBAAoB,KACZ,WAAY,KkBtuGlB,UAEF,iBAAA,gBnB6tGD,gBmB3tGG,aAAA,YnBiuGH,gBmB/tGG,gBAIA,MAAA,QlBuuGF,gBAAiB,UACjB,iBAAkB,YDNnB,0BmBhuGK,0BAUN,mCATM,mClB2uGJ,MAAO,KmB1yGP,gBAAA,KAGA,mBADA,QpBmyGD,QAAA,KAAA,KmBztGC,UAAW,KlBquGX,YAAa,UmBjzGb,cAAA,IAGA,mBADA,QpB0yGD,QAAA,IAAA,KmB5tGC,UAAW,KlBwuGX,YAAa,ImBxzGb,cAAA,IAGA,mBADA,QpBizGD,QAAA,IAAA,ImB3tGC,UAAW,KACX,YAAA,IACA,cAAA,IAIF,WACE,QAAA,MnB2tGD,MAAA,KCYD,sBACE,WAAY,IqBz3GZ,6BADF,4BtBk3GC,6BI7rGC,MAAA,KAEQ,MJisGT,QAAA,EsBr3GC,mBAAA,QAAA,KAAA,OACE,cAAA,QAAA,KAAA,OtBu3GH,WAAA,QAAA,KAAA,OsBl3GC,StBq3GD,QAAA,EsBn3Ga,UtBs3Gb,QAAA,KsBr3Ga,atBw3Gb,QAAA,MsBv3Ga,etB03Gb,QAAA,UsBt3GC,kBACA,QAAA,gBlBwKA,YACQ,SAAA,SAAA,OAAA,EAOR,SAAA,OACQ,mCAAA,KAAA,8BAAA,KAGR,2BAAA,KACQ,4BAAA,KAAA,uBAAA,KJ2sGT,oBAAA,KuBr5GC,4BAA6B,OAAQ,WACrC,uBAAA,OAAA,WACA,oBAAA,OAAA,WAEA,OACA,QAAA,aACA,MAAA,EACA,OAAA,EACA,YAAA,IACA,eAAA,OvBu5GD,WAAA,IAAA,OuBn5GC,WAAY,IAAI,QtBk6GhB,aAAc,IAAI,MAAM,YsBh6GxB,YAAA,IAAA,MAAA,YAKA,UADF,QvBo5GC,SAAA,SuB94GC,uBACA,QAAA,EAEA,eACA,SAAA,SACA,IAAA,KACA,KAAA,EACA,QAAA,KACA,QAAA,KACA,MAAA,KACA,UAAA,MACA,QAAA,IAAA,EACA,OAAA,IAAA,EAAA,EACA,UAAA,KACA,WAAA,KACA,WAAA,KnBsBA,iBAAA,KACQ,wBAAA,YmBrBR,gBAAA,YtB+5GA,OsB/5GA,IAAA,MAAA,KvBk5GD,OAAA,IAAA,MAAA,gBuB74GC,cAAA,IACE,mBAAA,EAAA,IAAA,KAAA,iBACA,WAAA,EAAA,IAAA,KAAA,iBAzBJ,0BCzBE,MAAA,EACA,KAAA,KAEA,wBxBo8GD,OAAA,IuB96GC,OAAQ,IAAI,EAmCV,SAAA,OACA,iBAAA,QAEA,oBACA,QAAA,MACA,QAAA,IAAA,KACA,MAAA,KvB84GH,YAAA,IuBx4GC,YAAA,WtBw5GA,MAAO,KsBt5GL,YAAA,OvB44GH,0BuB14GG,0BAMF,MAAA,QtBo5GA,gBAAiB,KACjB,iBAAkB,QsBj5GhB,yBAEA,+BADA,+BvBu4GH,MAAA,KuB73GC,gBAAA,KtB64GA,iBAAkB,QAClB,QAAS,EDZV,2BuB33GC,iCAAA,iCAEE,MAAA,KEzGF,iCF2GE,iCAEA,gBAAA,KvB63GH,OAAA,YuBx3GC,iBAAkB,YAGhB,iBAAA,KvBw3GH,OAAA,0DuBn3GG,qBvBs3GH,QAAA,MuB72GC,QACA,QAAA,EAQF,qBACE,MAAA,EACA,KAAA,KAIF,oBACE,MAAA,KACA,KAAA,EAEA,iBACA,QAAA,MACA,QAAA,IAAA,KvBw2GD,UAAA,KuBp2GC,YAAa,WACb,MAAA,KACA,YAAA,OAEA,mBACA,SAAA,MACA,IAAA,EvBs2GD,MAAA,EuBl2GC,OAAQ,EACR,KAAA,EACA,QAAA,IAQF,2BtB42GE,MAAO,EsBx2GL,KAAA,KAEA,eACA,sCvB41GH,QAAA,GuBn2GC,WAAY,EtBm3GZ,cAAe,IAAI,OsBx2GjB,cAAA,IAAA,QAEA,uBvB41GH,8CuBv0GC,IAAK,KAXL,OAAA,KApEA,cAAA,IvB25GC,yBuBv1GD,6BA1DA,MAAA,EACA,KAAA,KvBq5GD,kC0BpiHG,MAAO,KzBojHP,KAAM,GyBhjHR,W1BsiHD,oB0B1iHC,SAAU,SzB0jHV,QAAS,ayBpjHP,eAAA,OAGA,yB1BsiHH,gBCgBC,SAAU,SACV,MAAO,KyB7iHT,gC1BsiHC,gCCYD,+BAFA,+ByBhjHA,uBANM,uBzBujHN,sBAFA,sBAQE,QAAS,EyBljHP,qB1BuiHH,2B0BliHD,2BACE,iC1BoiHD,YAAA,KCgBD,aACE,YAAa,KDZd,kB0B1iHD,wBAAA,0BzB2jHE,MAAO,KDZR,kB0B/hHD,wBACE,0B1BiiHD,YAAA,I0B5hHC,yE1B+hHD,cAAA,E2BhlHC,4BACG,YAAA,EDsDL,mEzB6iHE,wBAAyB,E0B5lHzB,2BAAA,E3BilHD,6C0B5hHD,8CACE,uBAAA,E1B8hHD,0BAAA,E0B3hHC,sB1B8hHD,MAAA,KCgBD,8D0B/mHE,cAAA,E3BomHD,mE0B3hHD,oECjEE,wBAAA,EACG,2BAAA,EDqEL,oEzB0iHE,uBAAwB,EyBxiHxB,0BAAA,EAiBF,mCACE,iCACA,QAAA,EAEF,iCACE,cAAA,IACA,aAAA,IAKF,oCtB/CE,cAAA,KACQ,aAAA,KsBkDR,iCtBnDA,mBAAA,MAAA,EAAA,IAAA,IAAA,iBACQ,WAAA,MAAA,EAAA,IAAA,IAAA,iBsByDV,0CACE,mBAAA,K1BugHD,WAAA,K0BngHC,YACA,YAAA,EAGF,eACE,aAAA,IAAA,IAAA,E1BqgHD,oBAAA,ECgBD,uBACE,aAAc,EAAE,IAAI,IyB1gHlB,yBACA,+BACA,oC1B+/GH,QAAA,M0BtgHC,MAAO,KAcH,MAAA,K1B2/GL,UAAA,KCgBD,oCACE,MAAO,KyBpgHL,8BACA,oC1By/GH,oC0Bp/GC,0CACE,WAAA,K1Bs/GH,YAAA,E2B/pHC,4DACC,cAAA,EAQA,sD3B4pHF,uBAAA,I0Bt/GC,wBAAA,IC/KA,2BAAA,EACC,0BAAA,EAQA,sD3BkqHF,uBAAA,E0Bv/GC,wBAAyB,EACzB,2BAAA,I1By/GD,0BAAA,ICgBD,uE0BtrHE,cAAA,E3B2qHD,4E0Bt/GD,6EC7LE,2BAAA,EACC,0BAAA,EDoMH,6EACE,uBAAA,EACA,wBAAA,EAEA,qB1Bo/GD,QAAA,M0Bx/GC,MAAO,KzBwgHP,aAAc,MyBjgHZ,gBAAA,SAEA,0B1Bq/GH,gC0B9/GC,QAAS,WAYP,MAAA,K1Bq/GH,MAAA,G0Bj/GG,qC1Bo/GH,MAAA,KCgBD,+CACE,KAAM,KyB7+GF,gDAFA,6C1Bs+GL,2D0Br+GK,wDEzOJ,SAAU,SACV,KAAA,cACA,eAAA,K5BitHD,a4B7sHC,SAAA,SACE,QAAA,MACA,gBAAA,S5BgtHH,0B4BxtHC,MAAO,KAeL,cAAA,EACA,aAAA,EAOA,2BACA,SAAA,S5BusHH,QAAA,E4BrsHG,MAAA,KACE,MAAA,K5BusHL,cAAA,ECgBD,iCACE,QAAS,EiBnrHT,8BACA,mCACA,sCACA,OAAA,KlBwqHD,QAAA,KAAA,KkBtqHC,UAAA,KjBsrHA,YAAa,UACb,cAAe,IiBrrHb,oClB0qHH,yCkBvqHC,4CjBurHA,OAAQ,KACR,YAAa,KDTd,8C4B/sHD,mDAAA,sD3B0tHA,sCACA,2CiBzrHI,8CjB8rHF,OAAQ,KiB1sHR,8BACA,mCACA,sCACA,OAAA,KlB+rHD,QAAA,IAAA,KkB7rHC,UAAA,KjB6sHA,YAAa,IACb,cAAe,IiB5sHb,oClBisHH,yCkB9rHC,4CjB8sHA,OAAQ,KACR,YAAa,KDTd,8C4B7tHD,mDAAA,sD3BwuHA,sCACA,2CiBhtHI,8CjBqtHF,OAAQ,K2BzuHR,2B5B6tHD,mB4B7tHC,iB3B8uHA,QAAS,W2BzuHX,8D5B6tHC,sD4B7tHD,oDAEE,cAAA,EAEA,mB5B+tHD,iB4B1tHC,MAAO,GACP,YAAA,OACA,eAAA,OAEA,mBACA,QAAA,IAAA,KACA,UAAA,KACA,YAAA,IACA,YAAA,EACA,MAAA,K5B4tHD,WAAA,O4BztHC,iBAAA,KACE,OAAA,IAAA,MAAA,KACA,cAAA,I5B4tHH,4B4BztHC,QAAA,IAAA,KACE,UAAA,KACA,cAAA,I5B4tHH,4B4B/uHC,QAAS,KAAK,K3B+vHd,UAAW,K2BruHT,cAAA,IAKJ,wCAAA,qC3BquHE,WAAY,EAEd,uCACA,+BACA,kC0B70HE,6CACG,8CC4GL,6D5BqtHC,wE4BptHC,wBAAA,E5ButHD,2BAAA,ECgBD,+BACE,aAAc,EAEhB,sCACA,8B2BhuHA,+D5BstHC,oDCWD,iC0Bl1HE,4CACG,6CCiHH,uBAAA,E5BwtHD,0BAAA,E4BltHC,8BAGA,YAAA,E5BotHD,iB4BxtHC,SAAU,SAUR,UAAA,E5BitHH,YAAA,O4B/sHK,sB5BktHL,SAAA,SCgBD,2BACE,YAAa,K2BxtHb,6BAAA,4B5B4sHD,4B4BzsHK,QAAA,EAGJ,kCAAA,wCAGI,aAAA,K5B4sHL,iC6B12HD,uCACE,QAAA,EACA,YAAA,K7B62HD,K6B/2HC,aAAc,EAOZ,cAAA,EACA,WAAA,KARJ,QAWM,SAAA,SACA,QAAA,M7B42HL,U6B12HK,SAAA,S5B03HJ,QAAS,M4Bx3HH,QAAA,KAAA,KAMJ,gB7Bu2HH,gB6Bt2HK,gBAAA,K7By2HL,iBAAA,KCgBD,mB4Br3HQ,MAAA,KAGA,yBADA,yB7B02HP,MAAA,K6Bl2HG,gBAAA,K5Bk3HF,OAAQ,YACR,iBAAkB,Y4B/2Hd,aAzCN,mB7B64HC,mBwBh5HC,iBAAA,KACA,aAAA,QAEA,kBxBm5HD,OAAA,I6Bn5HC,OAAQ,IAAI,EA0DV,SAAA,O7B41HH,iBAAA,Q6Bl1HC,c7Bq1HD,UAAA,K6Bn1HG,UAEA,cAAA,IAAA,MAAA,KALJ,aASM,MAAA,KACA,cAAA,KAEA,e7Bo1HL,aAAA,I6Bn1HK,YAAA,WACE,OAAA,IAAA,MAAA,Y7Bq1HP,cAAA,IAAA,IAAA,EAAA,ECgBD,qBACE,aAAc,KAAK,KAAK,K4B51HlB,sBAEA,4BADA,4BAEA,MAAA,K7Bi1HP,OAAA,Q6B50HC,iBAAA,KAqDA,OAAA,IAAA,MAAA,KA8BA,oBAAA,YAnFA,wBAwDE,MAAA,K7B2xHH,cAAA,E6BzxHK,2BACA,MAAA,KA3DJ,6BAgEE,cAAA,IACA,WAAA,OAYJ,iDA0DE,IAAK,KAjED,KAAA,K7B0xHH,yB6BztHD,2BA9DM,QAAA,W7B0xHL,MAAA,G6Bn2HD,6BAuFE,cAAA,GAvFF,6B5Bw3HA,aAAc,EACd,cAAe,IDZhB,kC6BtuHD,wCA3BA,wCATM,OAAA,IAAA,MAAA,K7B+wHH,yB6B3uHD,6B5B2vHE,cAAe,IAAI,MAAM,KACzB,cAAe,IAAI,IAAI,EAAE,EDZ1B,kC6B92HD,wC7B+2HD,wC6B72HG,oBAAA,MAIE,c7B+2HL,MAAA,K6B52HK,gB7B+2HL,cAAA,ICgBD,iBACE,YAAa,I4Bv3HP,uBAQR,6B7Bo2HC,6B6Bl2HG,MAAA,K7Bq2HH,iBAAA,Q6Bn2HK,gBACA,MAAA,KAYN,mBACE,WAAA,I7B41HD,YAAA,E6Bz1HG,e7B41HH,MAAA,K6B11HK,kBACA,MAAA,KAPN,oBAYI,cAAA,IACA,WAAA,OAYJ,wCA0DE,IAAK,KAjED,KAAA,K7B21HH,yB6B1xHD,kBA9DM,QAAA,W7B21HL,MAAA,G6Bl1HD,oBACA,cAAA,GAIE,oBACA,cAAA,EANJ,yB5B02HE,aAAc,EACd,cAAe,IDZhB,8B6B1yHD,oCA3BA,oCATM,OAAA,IAAA,MAAA,K7Bm1HH,yB6B/yHD,yB5B+zHE,cAAe,IAAI,MAAM,KACzB,cAAe,IAAI,IAAI,EAAE,EDZ1B,8B6Bx0HD,oC7By0HD,oC6Bv0HG,oBAAA,MAGA,uB7B00HH,QAAA,K6B/zHC,qBF3OA,QAAA,M3B+iID,yB8BxiIC,WAAY,KACZ,uBAAA,EACA,wBAAA,EAEA,Q9B0iID,SAAA,S8BliIC,WAAY,KA8nBZ,cAAe,KAhoBb,OAAA,IAAA,MAAA,Y9ByiIH,yB8BzhIC,QAgnBE,cAAe,K9B86GlB,yB8BjhIC,eACA,MAAA,MAGA,iBACA,cAAA,KAAA,aAAA,KAEA,WAAA,Q9BkhID,2BAAA,M8BhhIC,WAAA,IAAA,MAAA,YACE,mBAAA,MAAA,EAAA,IAAA,EAAA,qB9BkhIH,WAAA,MAAA,EAAA,IAAA,EAAA,qB8Bz7GD,oBArlBI,WAAA,KAEA,yBAAA,iB9BkhID,MAAA,K8BhhIC,WAAA,EACE,mBAAA,KACA,WAAA,KAEA,0B9BkhIH,QAAA,gB8B/gIC,OAAA,eACE,eAAA,E9BihIH,SAAA,kBCkBD,oBACE,WAAY,QDZf,sC8B/gIK,mC9B8gIH,oC8BzgIC,cAAe,E7B4hIf,aAAc,G6Bj+GlB,sCAnjBE,mC7ByhIA,WAAY,MDdX,4D8BngID,sC9BogID,mCCkBG,WAAY,O6B3gId,kCANE,gC9BsgIH,4B8BvgIG,0BAuiBF,aAAc,M7Bm/Gd,YAAa,MAEf,yBDZC,kC8B3gIK,gC9B0gIH,4B8B3gIG,0BAcF,aAAc,EAChB,YAAA,GAMF,mBA8gBE,QAAS,KAhhBP,aAAA,EAAA,EAAA,I9BkgIH,yB8B7/HC,mB7B+gIE,cAAe,G6B1gIjB,qBADA,kB9BggID,SAAA,M8Bz/HC,MAAO,EAggBP,KAAM,E7B4gHN,QAAS,KDdR,yB8B7/HD,qB9B8/HD,kB8B7/HC,cAAA,GAGF,kBACE,IAAA,EACA,aAAA,EAAA,EAAA,I9BigID,qB8B1/HC,OAAQ,EACR,cAAA,EACA,aAAA,IAAA,EAAA,EAEA,cACA,MAAA,K9B4/HD,OAAA,K8B1/HC,QAAA,KAAA,K7B4gIA,UAAW,K6B1gIT,YAAA,KAIA,oBAbJ,oB9BwgIC,gBAAA,K8Bv/HG,kB7B0gIF,QAAS,MDdR,yBACF,iC8Bh/HC,uCACA,YAAA,OAGA,eC9LA,SAAA,SACA,MAAA,MD+LA,QAAA,IAAA,KACA,WAAA,IACA,aAAA,KACA,cAAA,I9Bm/HD,iBAAA,Y8B/+HC,iBAAA,KACE,OAAA,IAAA,MAAA,Y9Bi/HH,cAAA,I8B5+HG,qBACA,QAAA,EAEA,yB9B++HH,QAAA,M8BrgIC,MAAO,KAyBL,OAAA,I9B++HH,cAAA,I8BpjHD,mCAvbI,WAAA,I9Bg/HH,yB8Bt+HC,eACA,QAAA,MAGE,YACA,OAAA,MAAA,M9By+HH,iB8B58HC,YAAA,KA2YA,eAAgB,KAjaZ,YAAA,KAEA,yBACA,iCACA,SAAA,OACA,MAAA,KACA,MAAA,KAAA,WAAA,E9Bs+HH,iBAAA,Y8B3kHC,OAAQ,E7B8lHR,mBAAoB,K6Bt/HhB,WAAA,KAGA,kDAqZN,sC9BklHC,QAAA,IAAA,KAAA,IAAA,KCmBD,sC6Bv/HQ,YAAA,KAmBR,4C9Bs9HD,4C8BvlHG,iBAAkB,M9B4lHnB,yB8B5lHD,YAtYI,MAAA,K9Bq+HH,OAAA,E8Bn+HK,eACA,MAAA,K9Bu+HP,iB8B39HG,YAAa,KACf,eAAA,MAGA,aACA,QAAA,KAAA,K1B9NA,WAAA,IACQ,aAAA,M2B/DR,cAAA,IACA,YAAA,M/B4vID,WAAA,IAAA,MAAA,YiBtuHC,cAAe,IAAI,MAAM,YAwEzB,mBAAoB,MAAM,EAAE,IAAI,EAAE,qBAAyB,EAAE,IAAI,EAAE,qBAtI/D,WAAA,MAAA,EAAA,IAAA,EAAA,qBAAA,EAAA,IAAA,EAAA,qBAEA,yBjBwyHH,yBiBpqHC,QAAS,aA/HP,cAAA,EACA,eAAA,OjBuyHH,2BiBzqHC,QAAS,aAxHP,MAAA,KjBoyHH,eAAA,OiBhyHG,kCACA,QAAA,aAmHJ,0BhBmsHE,QAAS,aACT,eAAgB,OgB5yHd,wCjB6xHH,6CiBrrHD,2CjBwrHC,MAAA,KiB5xHG,wCACA,MAAA,KAmGJ,4BhB+sHE,cAAe,EgB3yHb,eAAA,OAGA,uBADA,oBjB6xHH,QAAA,aiBnsHC,WAAY,EhBstHZ,cAAe,EgB5yHX,eAAA,OAsFN,6BAAA,0BAjFI,aAAA,EAiFJ,4CjB4sHC,sCiBvxHG,SAAA,SjB0xHH,YAAA,E8BngID,kDAmWE,IAAK,GAvWH,yBACE,yB9B8gIL,cAAA,I8B5/HD,oCAoVE,cAAe,GA1Vf,yBACA,aACA,MAAA,KACA,YAAA,E1BzPF,eAAA,EACQ,aAAA,EJmwIP,YAAA,EACF,OAAA,E8BngIG,mBAAoB,KACtB,WAAA,M9BugID,8B8BngIC,WAAY,EACZ,uBAAA,EHzUA,wBAAA,EAQA,mDACC,cAAA,E3By0IF,uBAAA,I8B//HC,wBAAyB,IChVzB,2BAAA,EACA,0BAAA,EDkVA,YCnVA,WAAA,IACA,cAAA,IDqVA,mBCtVA,WAAA,KACA,cAAA,KD+VF,mBChWE,WAAA,KACA,cAAA,KDuWF,aAsSE,WAAY,KA1SV,cAAA,KAEA,yB9B+/HD,aACF,MAAA,K8Bl+HG,aAAc,KAhBhB,YAAA,MACA,yBE5WA,aF8WE,MAAA,eAFF,cAKI,MAAA,gB9Bu/HH,aAAA,M8B7+HD,4BACA,aAAA,GADF,gBAKI,iBAAA,Q9Bg/HH,aAAA,QCmBD,8B6BhgIM,MAAA,KARN,oC9B0/HC,oC8B5+HG,MAAA,Q9B++HH,iBAAA,Y8B1+HK,6B9B6+HL,MAAA,KCmBD,iC6B5/HQ,MAAA,KAKF,uC9By+HL,uCCmBC,MAAO,KACP,iBAAkB,Y6Bz/HZ,sCAIF,4C9Bu+HL,4CCmBC,MAAO,KACP,iBAAkB,Q6Bv/HZ,wCAxCR,8C9BihIC,8C8Bn+HG,MAAA,K9Bs+HH,iBAAA,YCmBD,+B6Bt/HM,aAAA,KAGA,qCApDN,qC9B2hIC,iBAAA,KCmBD,yC6Bp/HI,iBAAA,KAOE,iCAAA,6B7Bk/HJ,aAAc,Q6B9+HR,oCAiCN,0C9B+7HD,0C8B3xHC,MAAO,KA7LC,iBAAA,QACA,yB7B8+HR,sD6B5+HU,MAAA,KAKF,4D9By9HP,4DCmBC,MAAO,KACP,iBAAkB,Y6Bz+HV,2DAIF,iE9Bu9HP,iECmBC,MAAO,KACP,iBAAkB,Q6Bv+HV,6D9B09HX,mEADE,mE8B1jIC,MAAO,KA8GP,iBAAA,aAEE,6B9Bi9HL,MAAA,K8B58HG,mC9B+8HH,MAAA,KCmBD,0B6B/9HM,MAAA,KAIA,gCAAA,gC7Bg+HJ,MAAO,K6Bt9HT,0CARQ,0CASN,mD9Bu8HD,mD8Bt8HC,MAAA,KAFF,gBAKI,iBAAA,K9B08HH,aAAA,QCmBD,8B6B19HM,MAAA,QARN,oC9Bo9HC,oC8Bt8HG,MAAA,K9By8HH,iBAAA,Y8Bp8HK,6B9Bu8HL,MAAA,QCmBD,iC6Bt9HQ,MAAA,QAKF,uC9Bm8HL,uCCmBC,MAAO,KACP,iBAAkB,Y6Bn9HZ,sCAIF,4C9Bi8HL,4CCmBC,MAAO,KACP,iBAAkB,Q6Bj9HZ,wCAxCR,8C9B2+HC,8C8B57HG,MAAA,K9B+7HH,iBAAA,YCmBD,+B6B/8HM,aAAA,KAGA,qCArDN,qC9Bq/HC,iBAAA,KCmBD,yC6B78HI,iBAAA,KAME,iCAAA,6B7B48HJ,aAAc,Q6Bx8HR,oCAuCN,0C9Bm5HD,0C8B33HC,MAAO,KAvDC,iBAAA,QAuDV,yBApDU,kE9Bs7HP,aAAA,Q8Bn7HO,0D9Bs7HP,iBAAA,QCmBD,sD6Bt8HU,MAAA,QAKF,4D9Bm7HP,4DCmBC,MAAO,KACP,iBAAkB,Y6Bn8HV,2DAIF,iE9Bi7HP,iECmBC,MAAO,KACP,iBAAkB,Q6Bj8HV,6D9Bo7HX,mEADE,mE8B1hIC,MAAO,KA+GP,iBAAA,aAEE,6B9Bg7HL,MAAA,Q8B36HG,mC9B86HH,MAAA,KCmBD,0B6B97HM,MAAA,QAIA,gCAAA,gC7B+7HJ,MAAO,KgCvkJT,0CH0oBQ,0CGzoBN,mDjCwjJD,mDiCvjJC,MAAA,KAEA,YACA,QAAA,IAAA,KjC2jJD,cAAA,KiChkJC,WAAY,KAQV,iBAAA,QjC2jJH,cAAA,IiCxjJK,eACA,QAAA,ajC4jJL,yBiCxkJC,QAAS,EAAE,IAkBT,MAAA,KjCyjJH,QAAA,SkC5kJC,oBACA,MAAA,KAEA,YlC+kJD,QAAA,akCnlJC,aAAc,EAOZ,OAAA,KAAA,ElC+kJH,cAAA,ICmBD,eiC/lJM,QAAA,OAEA,iBACA,oBACA,SAAA,SACA,MAAA,KACA,QAAA,IAAA,KACA,YAAA,KACA,YAAA,WlCglJL,MAAA,QkC9kJG,gBAAA,KjCimJF,iBAAkB,KiC9lJZ,OAAA,IAAA,MAAA,KPVH,6B3B2lJJ,gCkC7kJG,YAAA,EjCgmJF,uBAAwB,I0BvnJxB,0BAAA,I3BymJD,4BkCxkJG,+BjC2lJF,wBAAyB,IACzB,2BAA4B,IiCxlJxB,uBAFA,uBAGA,0BAFA,0BlC8kJL,QAAA,EkCtkJG,MAAA,QjCylJF,iBAAkB,KAClB,aAAc,KAEhB,sBiCvlJM,4BAFA,4BjC0lJN,yBiCvlJM,+BAFA,+BAGA,QAAA,ElC2kJL,MAAA,KkCloJC,OAAQ,QjCqpJR,iBAAkB,QAClB,aAAc,QiCnlJV,wBAEA,8BADA,8BjColJN,2BiCtlJM,iCjCulJN,iCDZC,MAAA,KkC/jJC,OAAQ,YjCklJR,iBAAkB,KkC7pJd,aAAA,KAEA,oBnC8oJL,uBmC5oJG,QAAA,KAAA,KlC+pJF,UAAW,K0B1pJX,YAAA,U3B4oJD,gCmC3oJG,mClC8pJF,uBAAwB,I0BvqJxB,0BAAA,I3BypJD,+BkC1kJD,kCjC6lJE,wBAAyB,IkC7qJrB,2BAAA,IAEA,oBnC8pJL,uBmC5pJG,QAAA,IAAA,KlC+qJF,UAAW,K0B1qJX,YAAA,I3B4pJD,gCmC3pJG,mClC8qJF,uBAAwB,I0BvrJxB,0BAAA,I3ByqJD,+BoC3qJD,kCACE,wBAAA,IACA,2BAAA,IAEA,OpC6qJD,aAAA,EoCjrJC,OAAQ,KAAK,EAOX,WAAA,OpC6qJH,WAAA,KCmBD,UmC7rJM,QAAA,OAEA,YACA,eACA,QAAA,apC8qJL,QAAA,IAAA,KoC5rJC,iBAAkB,KnC+sJlB,OAAQ,IAAI,MAAM,KmC5rJd,cAAA,KAnBN,kBpCisJC,kBCmBC,gBAAiB,KmCzrJb,iBAAA,KA3BN,eAAA,kBAkCM,MAAA,MAlCN,mBAAA,sBnC6tJE,MAAO,KmClrJH,mBAEA,yBADA,yBpCqqJL,sBqCltJC,MAAO,KACP,OAAA,YACA,iBAAA,KAEA,OACA,QAAA,OACA,QAAA,KAAA,KAAA,KACA,UAAA,IACA,YAAA,IACA,YAAA,EACA,MAAA,KrCotJD,WAAA,OqChtJG,YAAA,OpCmuJF,eAAgB,SoCjuJZ,cAAA,MrCotJL,cqCltJK,cAKJ,MAAA,KACE,gBAAA,KrC+sJH,OAAA,QqC1sJG,aACA,QAAA,KAOJ,YCtCE,SAAA,StC+uJD,IAAA,KCmBD,eqC7vJM,iBAAA,KALJ,2BD0CF,2BrC4sJC,iBAAA,QCmBD,eqCpwJM,iBAAA,QALJ,2BD8CF,2BrC+sJC,iBAAA,QCmBD,eqC3wJM,iBAAA,QALJ,2BDkDF,2BrCktJC,iBAAA,QCmBD,YqClxJM,iBAAA,QALJ,wBDsDF,wBrCqtJC,iBAAA,QCmBD,eqCzxJM,iBAAA,QALJ,2BD0DF,2BrCwtJC,iBAAA,QCmBD,cqChyJM,iBAAA,QCDJ,0BADF,0BAEE,iBAAA,QAEA,OACA,QAAA,aACA,UAAA,KACA,QAAA,IAAA,IACA,UAAA,KACA,YAAA,IACA,YAAA,EACA,MAAA,KACA,WAAA,OvCqxJD,YAAA,OuClxJC,eAAA,OACE,iBAAA,KvCoxJH,cAAA,KuC/wJG,aACA,QAAA,KAGF,YtCkyJA,SAAU,SsChyJR,IAAA,KAMA,0BvC4wJH,eCmBC,IAAK,EsC7xJD,QAAA,IAAA,IvCgxJL,cuC9wJK,cAKJ,MAAA,KtC4xJA,gBAAiB,KsC1xJf,OAAA,QvC4wJH,+BuCxwJC,4BACE,MAAA,QvC0wJH,iBAAA,KuCtwJG,wBvCywJH,MAAA,MuCrwJG,+BvCwwJH,aAAA,IwCj0JC,uBACA,YAAA,IAEA,WACA,YAAA,KxCo0JD,eAAA,KwCz0JC,cAAe,KvC41Jf,MAAO,QuCn1JL,iBAAA,KAIA,eAbJ,cAcI,MAAA,QxCo0JH,awCl1JC,cAAe,KAmBb,UAAA,KxCk0JH,YAAA,ICmBD,cuCh1JI,iBAAA,QAEA,sBxCi0JH,4BwC31JC,cAAe,KA8Bb,aAAA,KxCg0JH,cAAA,IwC7yJD,sBAfI,UAAA,KxCi0JD,oCwC9zJC,WvCi1JA,YAAa,KuC/0JX,eAAA,KxCi0JH,sBwCvzJD,4BvC00JE,cAAe,KuC90Jb,aAAA,KC5CJ,ezC42JD,cyC32JC,UAAA,MAGA,WACA,QAAA,MACA,QAAA,IACA,cAAA,KrCiLA,YAAA,WACK,iBAAA,KACG,OAAA,IAAA,MAAA,KJ8rJT,cAAA,IyCx3JC,mBAAoB,OAAO,IAAI,YxC24J1B,cAAe,OAAO,IAAI,YwC93J7B,WAAA,OAAA,IAAA,YAKF,iBzC22JD,eCmBC,aAAc,KACd,YAAa,KwCv3JX,mBA1BJ,kBzCk4JC,kByCv2JG,aAAA,QCzBJ,oBACE,QAAA,IACA,MAAA,KAEA,O1Cs4JD,QAAA,K0C14JC,cAAe,KAQb,OAAA,IAAA,MAAA,YAEA,cAAA,IAVJ,UAeI,WAAA,E1Ck4JH,MAAA,QCmBD,mByC/4JI,YAAA,IArBJ,SAyBI,U1C+3JH,cAAA,ECmBD,WyCx4JE,WAAA,IAFF,mBAAA,mBAMI,cAAA,KAEA,0BACA,0B1Cy3JH,SAAA,S0Cj3JC,IAAK,KCvDL,MAAA,MACA,MAAA,Q3C46JD,e0Ct3JC,MAAO,QClDL,iBAAA,Q3C26JH,aAAA,Q2Cx6JG,kB3C26JH,iBAAA,Q2Cn7JC,2BACA,MAAA,Q3Cu7JD,Y0C73JC,MAAO,QCtDL,iBAAA,Q3Cs7JH,aAAA,Q2Cn7JG,e3Cs7JH,iBAAA,Q2C97JC,wBACA,MAAA,Q3Ck8JD,e0Cp4JC,MAAO,QC1DL,iBAAA,Q3Ci8JH,aAAA,Q2C97JG,kB3Ci8JH,iBAAA,Q2Cz8JC,2BACA,MAAA,Q3C68JD,c0C34JC,MAAO,QC9DL,iBAAA,Q3C48JH,aAAA,Q2Cz8JG,iB3C48JH,iBAAA,Q4C78JC,0BAAQ,MAAA,QACR,wCAAQ,K5Cm9JP,oBAAA,KAAA,E4C/8JD,GACA,oBAAA,EAAA,GACA,mCAAQ,K5Cq9JP,oBAAA,KAAA,E4Cv9JD,GACA,oBAAA,EAAA,GACA,gCAAQ,K5Cq9JP,oBAAA,KAAA,E4C78JD,GACA,oBAAA,EAAA,GAGA,UACA,OAAA,KxCsCA,cAAA,KACQ,SAAA,OJ26JT,iBAAA,Q4C78JC,cAAe,IACf,mBAAA,MAAA,EAAA,IAAA,IAAA,eACA,WAAA,MAAA,EAAA,IAAA,IAAA,eAEA,cACA,MAAA,KACA,MAAA,EACA,OAAA,KACA,UAAA,KxCyBA,YAAA,KACQ,MAAA,KAyHR,WAAA,OACK,iBAAA,QACG,mBAAA,MAAA,EAAA,KAAA,EAAA,gBJ+zJT,WAAA,MAAA,EAAA,KAAA,EAAA,gB4C18JC,mBAAoB,MAAM,IAAI,K3Cq+JzB,cAAe,MAAM,IAAI,K4Cp+J5B,WAAA,MAAA,IAAA,KDEF,sBCAE,gCDAF,iBAAA,yK5C88JD,iBAAA,oK4Cv8JC,iBAAiB,iK3Cm+JjB,wBAAyB,KAAK,KG/gK9B,gBAAA,KAAA,KJy/JD,qBIv/JS,+BwCmDR,kBAAmB,qBAAqB,GAAG,OAAO,SErElD,aAAA,qBAAA,GAAA,OAAA,S9C4gKD,UAAA,qBAAA,GAAA,OAAA,S6Cz9JG,sBACA,iBAAA,Q7C69JH,wC4Cx8JC,iBAAkB,yKEzElB,iBAAA,oK9CohKD,iBAAA,iK6Cj+JG,mBACA,iBAAA,Q7Cq+JH,qC4C58JC,iBAAkB,yKE7ElB,iBAAA,oK9C4hKD,iBAAA,iK6Cz+JG,sBACA,iBAAA,Q7C6+JH,wC4Ch9JC,iBAAkB,yKEjFlB,iBAAA,oK9CoiKD,iBAAA,iK6Cj/JG,qBACA,iBAAA,Q7Cq/JH,uC+C5iKC,iBAAkB,yKAElB,iBAAA,oK/C6iKD,iBAAA,iK+C1iKG,O/C6iKH,WAAA,KC4BD,mB8CnkKE,WAAA,E/C4iKD,O+CxiKD,YACE,SAAA,O/C0iKD,KAAA,E+CtiKC,Y/CyiKD,MAAA,Q+CriKG,c/CwiKH,QAAA,MC4BD,4B8C9jKE,UAAA,KAGF,aAAA,mBAEE,aAAA,KAGF,YAAA,kB9C+jKE,cAAe,K8CxjKjB,YAHE,Y/CoiKD,a+ChiKC,QAAA,W/CmiKD,eAAA,I+C/hKC,c/CkiKD,eAAA,O+C7hKC,cACA,eAAA,OAMF,eACE,WAAA,EACA,cAAA,ICvDF,YAEE,aAAA,EACA,WAAA,KAQF,YACE,aAAA,EACA,cAAA,KAGA,iBACA,SAAA,SACA,QAAA,MhD6kKD,QAAA,KAAA,KgD1kKC,cAAA,KrB3BA,iBAAA,KACC,OAAA,IAAA,MAAA,KqB6BD,6BACE,uBAAA,IrBvBF,wBAAA,I3BsmKD,4BgDpkKC,cAAe,E/CgmKf,2BAA4B,I+C9lK5B,0BAAA,IAFF,kBAAA,uBAKI,MAAA,KAIF,2CAAA,gD/CgmKA,MAAO,K+C5lKL,wBAFA,wBhDykKH,6BgDxkKG,6BAKF,MAAO,KACP,gBAAA,KACA,iBAAA,QAKA,uB/C4lKA,MAAO,KACP,WAAY,K+CzlKV,0BhDmkKH,gCgDlkKG,gCALF,MAAA,K/CmmKA,OAAQ,YACR,iBAAkB,KDxBnB,mDgD5kKC,yDAAA,yD/CymKA,MAAO,QDxBR,gDgDhkKC,sDAAA,sD/C6lKA,MAAO,K+CzlKL,wBAEA,8BADA,8BhDmkKH,QAAA,EgDxkKC,MAAA,K/ComKA,iBAAkB,QAClB,aAAc,QAEhB,iDDpBC,wDCuBD,uDADA,uD+CzmKE,8DAYI,6D/C4lKN,uD+CxmKE,8D/C2mKF,6DAKE,MAAO,QDxBR,8CiD1qKG,oDADF,oDAEE,MAAA,QAEA,yBhDusKF,MAAO,QgDrsKH,iBAAA,QAFF,0BAAA,+BAKI,MAAA,QAGF,mDAAA,wDhDwsKJ,MAAO,QDtBR,gCiDhrKO,gCAGF,qCAFE,qChD2sKN,MAAO,QACP,iBAAkB,QAEpB,iCgDvsKQ,uCAFA,uChD0sKR,sCDtBC,4CiDnrKO,4CArBN,MAAA,KACE,iBAAA,QACA,aAAA,QAEA,sBhDouKF,MAAO,QgDluKH,iBAAA,QAFF,uBAAA,4BAKI,MAAA,QAGF,gDAAA,qDhDquKJ,MAAO,QDtBR,6BiD7sKO,6BAGF,kCAFE,kChDwuKN,MAAO,QACP,iBAAkB,QAEpB,8BgDpuKQ,oCAFA,oChDuuKR,mCDtBC,yCiDhtKO,yCArBN,MAAA,KACE,iBAAA,QACA,aAAA,QAEA,yBhDiwKF,MAAO,QgD/vKH,iBAAA,QAFF,0BAAA,+BAKI,MAAA,QAGF,mDAAA,wDhDkwKJ,MAAO,QDtBR,gCiD1uKO,gCAGF,qCAFE,qChDqwKN,MAAO,QACP,iBAAkB,QAEpB,iCgDjwKQ,uCAFA,uChDowKR,sCDtBC,4CiD7uKO,4CArBN,MAAA,KACE,iBAAA,QACA,aAAA,QAEA,wBhD8xKF,MAAO,QgD5xKH,iBAAA,QAFF,yBAAA,8BAKI,MAAA,QAGF,kDAAA,uDhD+xKJ,MAAO,QDtBR,+BiDvwKO,+BAGF,oCAFE,oChDkyKN,MAAO,QACP,iBAAkB,QAEpB,gCgD9xKQ,sCAFA,sChDiyKR,qCDtBC,2CiD1wKO,2CDkGN,MAAO,KACP,iBAAA,QACA,aAAA,QAEF,yBACE,WAAA,EACA,cAAA,IE1HF,sBACE,cAAA,EACA,YAAA,IAEA,O9C0DA,cAAA,KACQ,iBAAA,KJ6uKT,OAAA,IAAA,MAAA,YkDnyKC,cAAe,IACf,mBAAA,EAAA,IAAA,IAAA,gBlDqyKD,WAAA,EAAA,IAAA,IAAA,gBkD/xKC,YACA,QAAA,KvBnBC,e3BuzKF,QAAA,KAAA,KkDtyKC,cAAe,IAAI,MAAM,YAMvB,uBAAA,IlDmyKH,wBAAA,IkD7xKC,0CACA,MAAA,QAEA,alDgyKD,WAAA,EkDpyKC,cAAe,EjDg0Kf,UAAW,KACX,MAAO,QDtBR,oBkD1xKC,sBjDkzKF,eiDxzKI,mBAKJ,qBAEE,MAAA,QvBvCA,cACC,QAAA,KAAA,K3Bs0KF,iBAAA,QkDrxKC,WAAY,IAAI,MAAM,KjDizKtB,2BAA4B,IiD9yK1B,0BAAA,IAHJ,mBAAA,mCAMM,cAAA,ElDwxKL,oCkDnxKG,oDjD+yKF,aAAc,IAAI,EiD7yKZ,cAAA,EvBtEL,4D3B61KF,4EkDjxKG,WAAA,EjD6yKF,uBAAwB,IiD3yKlB,wBAAA,IvBtEL,0D3B21KF,0EkD1yKC,cAAe,EvB1Df,2BAAA,IACC,0BAAA,IuB0FH,+EAEI,uBAAA,ElD8wKH,wBAAA,EkD1wKC,wDlD6wKD,iBAAA,EC4BD,0BACE,iBAAkB,EiDlyKpB,8BlD0wKC,ckD1wKD,gCjDuyKE,cAAe,EiDvyKjB,sCAQM,sBlDwwKL,wCC4BC,cAAe,K0Br5Kf,aAAA,KuByGF,wDlDqxKC,0BC4BC,uBAAwB,IACxB,wBAAyB,IiDlzK3B,yFAoBQ,yFlDwwKP,2DkDzwKO,2DjDqyKN,uBAAwB,IACxB,wBAAyB,IAK3B,wGiD9zKA,wGjD4zKA,wGDtBC,wGCuBD,0EiD7zKA,0EjD2zKA,0EiDnyKU,0EjD2yKR,uBAAwB,IAK1B,uGiDx0KA,uGjDs0KA,uGDtBC,uGCuBD,yEiDv0KA,yEjDq0KA,yEiDzyKU,yEvB7HR,wBAAA,IuBiGF,sDlDqzKC,yBC4BC,2BAA4B,IAC5B,0BAA2B,IiDxyKrB,qFA1CR,qFAyCQ,wDlDmxKP,wDC4BC,2BAA4B,IAC5B,0BAA2B,IAG7B,oGDtBC,oGCwBD,oGiD91KA,oGjD21KA,uEiD7yKU,uEjD+yKV,uEiD71KA,uEjDm2KE,0BAA2B,IAG7B,mGDtBC,mGCwBD,mGiDx2KA,mGjDq2KA,sEiDnzKU,sEjDqzKV,sEiDv2KA,sEjD62KE,2BAA4B,IiDlzK1B,0BlD2xKH,qCkDt1KD,0BAAA,qCA+DI,WAAA,IAAA,MAAA,KA/DJ,kDAAA,kDAmEI,WAAA,EAnEJ,uBAAA,yCjD23KE,OAAQ,EiDjzKA,+CjDqzKV,+CiD/3KA,+CjDi4KA,+CAEA,+CANA,+CDjBC,iECoBD,iEiDh4KA,iEjDk4KA,iEAEA,iEANA,iEAWE,YAAa,EiD3zKL,8CjD+zKV,8CiD74KA,8CjD+4KA,8CAEA,8CANA,8CDjBC,gECoBD,gEiD94KA,gEjDg5KA,gEAEA,gEANA,gEAWE,aAAc,EAIhB,+CiD35KA,+CjDy5KA,+CiDl0KU,+CjDq0KV,iEiD55KA,iEjD05KA,iEDtBC,iEC6BC,cAAe,EAEjB,8CiDn0KU,8CjDq0KV,8CiDr6KA,8CjDo6KA,gEDtBC,gECwBD,gEiDh0KI,gEACA,cAAA,EAUJ,yBACE,cAAA,ElDmyKD,OAAA,EkD/xKG,aACA,cAAA,KANJ,oBASM,cAAA,ElDkyKL,cAAA,IkD7xKG,2BlDgyKH,WAAA,IC4BD,4BiDxzKM,cAAA,EAKF,wDAvBJ,wDlDqzKC,WAAA,IAAA,MAAA,KkD5xKK,2BlD+xKL,WAAA,EmDlhLC,uDnDqhLD,cAAA,IAAA,MAAA,KmDlhLG,eACA,aAAA,KnDshLH,8BmDxhLC,MAAA,KAMI,iBAAA,QnDqhLL,aAAA,KmDlhLK,0DACA,iBAAA,KAGJ,qCAEI,MAAA,QnDmhLL,iBAAA,KmDpiLC,yDnDuiLD,oBAAA,KmDpiLG,eACA,aAAA,QnDwiLH,8BmD1iLC,MAAA,KAMI,iBAAA,QnDuiLL,aAAA,QmDpiLK,0DACA,iBAAA,QAGJ,qCAEI,MAAA,QnDqiLL,iBAAA,KmDtjLC,yDnDyjLD,oBAAA,QmDtjLG,eACA,aAAA,QnD0jLH,8BmD5jLC,MAAA,QAMI,iBAAA,QnDyjLL,aAAA,QmDtjLK,0DACA,iBAAA,QAGJ,qCAEI,MAAA,QnDujLL,iBAAA,QmDxkLC,yDnD2kLD,oBAAA,QmDxkLG,YACA,aAAA,QnD4kLH,2BmD9kLC,MAAA,QAMI,iBAAA,QnD2kLL,aAAA,QmDxkLK,uDACA,iBAAA,QAGJ,kCAEI,MAAA,QnDykLL,iBAAA,QmD1lLC,sDnD6lLD,oBAAA,QmD1lLG,eACA,aAAA,QnD8lLH,8BmDhmLC,MAAA,QAMI,iBAAA,QnD6lLL,aAAA,QmD1lLK,0DACA,iBAAA,QAGJ,qCAEI,MAAA,QnD2lLL,iBAAA,QmD5mLC,yDnD+mLD,oBAAA,QmD5mLG,cACA,aAAA,QnDgnLH,6BmDlnLC,MAAA,QAMI,iBAAA,QnD+mLL,aAAA,QmD5mLK,yDACA,iBAAA,QAGJ,oCAEI,MAAA,QnD6mLL,iBAAA,QoD5nLC,wDACA,oBAAA,QAEA,kBACA,SAAA,SpD+nLD,QAAA,MoDpoLC,OAAQ,EnDgqLR,QAAS,EACT,SAAU,OAEZ,yCmDtpLI,wBADA,yBAEA,yBACA,wBACA,SAAA,SACA,IAAA,EACA,OAAA,EpD+nLH,KAAA,EoD1nLC,MAAO,KACP,OAAA,KpD4nLD,OAAA,EoDvnLC,wBpD0nLD,eAAA,OqDppLC,uBACA,eAAA,IAEA,MACA,WAAA,KACA,QAAA,KjDwDA,cAAA,KACQ,iBAAA,QJgmLT,OAAA,IAAA,MAAA,QqD/pLC,cAAe,IASb,mBAAA,MAAA,EAAA,IAAA,IAAA,gBACA,WAAA,MAAA,EAAA,IAAA,IAAA,gBAKJ,iBACE,aAAA,KACA,aAAA,gBAEF,SACE,QAAA,KACA,cAAA,ICtBF,SACE,QAAA,IACA,cAAA,IAEA,OACA,MAAA,MACA,UAAA,KjCRA,YAAA,IAGA,YAAA,ErBqrLD,MAAA,KsD7qLC,YAAA,EAAA,IAAA,EAAA,KrDysLA,OAAQ,kBqDvsLN,QAAA,GjCbF,aiCeE,ajCZF,MAAA,KrB6rLD,gBAAA,KsDzqLC,OAAA,QACE,OAAA,kBACA,QAAA,GAEA,aACA,mBAAA,KtD2qLH,QAAA,EuDhsLC,OAAQ,QACR,WAAA,IvDksLD,OAAA,EuD7rLC,YACA,SAAA,OAEA,OACA,SAAA,MACA,IAAA,EACA,MAAA,EACA,OAAA,EACA,KAAA,EAIA,QAAA,KvD6rLD,QAAA,KuD1rLC,SAAA,OnD+GA,2BAAA,MACI,QAAA,EAEI,0BAkER,mBAAA,kBAAA,IAAA,SAEK,cAAA,aAAA,IAAA,SACG,WAAA,UAAA,IAAA,SJ6gLT,kBAAA,kBuDhsLC,cAAA,kBnD2GA,aAAA,kBACI,UAAA,kBAEI,wBJwlLT,kBAAA,euDpsLK,cAAe,eACnB,aAAA,eACA,UAAA,eAIF,mBACE,WAAA,OACA,WAAA,KvDqsLD,cuDhsLC,SAAU,SACV,MAAA,KACA,OAAA,KAEA,eACA,SAAA,SnDaA,iBAAA,KACQ,wBAAA,YmDZR,gBAAA,YtD4tLA,OsD5tLA,IAAA,MAAA,KAEA,OAAA,IAAA,MAAA,evDksLD,cAAA,IuD9rLC,QAAS,EACT,mBAAA,EAAA,IAAA,IAAA,eACA,WAAA,EAAA,IAAA,IAAA,eAEA,gBACA,SAAA,MACA,IAAA,EACA,MAAA,EvDgsLD,OAAA,EuD9rLC,KAAA,ElCrEA,QAAA,KAGA,iBAAA,KkCmEA,qBlCtEA,OAAA,iBAGA,QAAA,EkCwEF,mBACE,OAAA,kBACA,QAAA,GAIF,cACE,QAAA,KvDgsLD,cAAA,IAAA,MAAA,QuD3rLC,qBACA,WAAA,KAKF,aACE,OAAA,EACA,YAAA,WAIF,YACE,SAAA,SACA,QAAA,KvD0rLD,cuD5rLC,QAAS,KAQP,WAAA,MACA,WAAA,IAAA,MAAA,QATJ,wBAaI,cAAA,EvDsrLH,YAAA,IuDlrLG,mCvDqrLH,YAAA,KuD/qLC,oCACA,YAAA,EAEA,yBACA,SAAA,SvDkrLD,IAAA,QuDhqLC,MAAO,KAZP,OAAA,KACE,SAAA,OvDgrLD,yBuD7qLD,cnDvEA,MAAA,MACQ,OAAA,KAAA,KmD2ER,eAAY,mBAAA,EAAA,IAAA,KAAA,evD+qLX,WAAA,EAAA,IAAA,KAAA,euDzqLD,UAFA,MAAA,OvDirLD,yBwD/zLC,UACA,MAAA,OCNA,SAEA,SAAA,SACA,QAAA,KACA,QAAA,MACA,YAAA,iBAAA,UAAA,MAAA,WACA,UAAA,KACA,WAAA,OACA,YAAA,IACA,YAAA,WACA,WAAA,KACA,WAAA,MACA,gBAAA,KACA,YAAA,KACA,eAAA,KACA,eAAA,ODHA,WAAA,OnCVA,aAAA,OAGA,UAAA,OrBs1LD,YAAA,OwD30LC,OAAA,iBnCdA,QAAA,ErB61LD,WAAA,KwD90LY,YAAmB,OAAA,kBxDk1L/B,QAAA,GwDj1LY,aAAmB,QAAA,IAAA,ExDq1L/B,WAAA,KwDp1LY,eAAmB,QAAA,EAAA,IxDw1L/B,YAAA,IwDv1LY,gBAAmB,QAAA,IAAA,ExD21L/B,WAAA,IwDt1LC,cACA,QAAA,EAAA,IACA,YAAA,KAEA,eACA,UAAA,MxDy1LD,QAAA,IAAA,IwDr1LC,MAAO,KACP,WAAA,OACA,iBAAA,KACA,cAAA,IAEA,exDu1LD,SAAA,SwDn1LC,MAAA,EACE,OAAA,EACA,aAAA,YACA,aAAA,MAEA,4BxDq1LH,OAAA,EwDn1LC,KAAA,IACE,YAAA,KACA,aAAA,IAAA,IAAA,EACA,iBAAA,KAEA,iCxDq1LH,MAAA,IwDn1LC,OAAA,EACE,cAAA,KACA,aAAA,IAAA,IAAA,EACA,iBAAA,KAEA,kCxDq1LH,OAAA,EwDn1LC,KAAA,IACE,cAAA,KACA,aAAA,IAAA,IAAA,EACA,iBAAA,KAEA,8BxDq1LH,IAAA,IwDn1LC,KAAA,EACE,WAAA,KACA,aAAA,IAAA,IAAA,IAAA,EACA,mBAAA,KAEA,6BxDq1LH,IAAA,IwDn1LC,MAAA,EACE,WAAA,KACA,aAAA,IAAA,EAAA,IAAA,IACA,kBAAA,KAEA,+BxDq1LH,IAAA,EwDn1LC,KAAA,IACE,YAAA,KACA,aAAA,EAAA,IAAA,IACA,oBAAA,KAEA,oCxDq1LH,IAAA,EwDn1LC,MAAA,IACE,WAAA,KACA,aAAA,EAAA,IAAA,IACA,oBAAA,KAEA,qCxDq1LH,IAAA,E0Dl7LC,KAAM,IACN,WAAA,KACA,aAAA,EAAA,IAAA,IACA,oBAAA,KAEA,SACA,SAAA,SACA,IAAA,EDXA,KAAA,EAEA,QAAA,KACA,QAAA,KACA,UAAA,MACA,QAAA,IACA,YAAA,iBAAA,UAAA,MAAA,WACA,UAAA,KACA,WAAA,OACA,YAAA,IACA,YAAA,WACA,WAAA,KACA,WAAA,MACA,gBAAA,KACA,YAAA,KACA,eAAA,KCAA,eAAA,OAEA,WAAA,OACA,aAAA,OAAA,UAAA,OACA,YAAA,OACA,iBAAA,KACA,wBAAA,YtD8CA,gBAAA,YACQ,OAAA,IAAA,MAAA,KJk5LT,OAAA,IAAA,MAAA,e0D77LC,cAAA,IAAY,mBAAA,EAAA,IAAA,KAAA,e1Dg8Lb,WAAA,EAAA,IAAA,KAAA,e0D/7La,WAAA,KACZ,aAAY,WAAA,MACZ,eAAY,YAAA,KAGd,gBACE,WAAA,KAEA,cACA,YAAA,MAEA,e1Dq8LD,QAAA,IAAA,K0Dl8LC,OAAQ,EACR,UAAA,K1Do8LD,iBAAA,Q0D57LC,cAAA,IAAA,MAAA,QzDy9LA,cAAe,IAAI,IAAI,EAAE,EyDt9LvB,iBACA,QAAA,IAAA,KAEA,gBACA,sB1D87LH,SAAA,S0D37LC,QAAS,MACT,MAAA,E1D67LD,OAAA,E0D37LC,aAAc,YACd,aAAA,M1D87LD,gB0Dz7LC,aAAA,KAEE,sBACA,QAAA,GACA,aAAA,KAEA,oB1D27LH,OAAA,M0D17LG,KAAA,IACE,YAAA,MACA,iBAAA,KACA,iBAAA,gBACA,oBAAA,E1D67LL,0B0Dz7LC,OAAA,IACE,YAAA,MACA,QAAA,IACA,iBAAA,KACA,oBAAA,EAEA,sB1D27LH,IAAA,I0D17LG,KAAA,MACE,WAAA,MACA,mBAAA,KACA,mBAAA,gBACA,kBAAA,E1D67LL,4B0Dz7LC,OAAA,MACE,KAAA,IACA,QAAA,IACA,mBAAA,KACA,kBAAA,EAEA,uB1D27LH,IAAA,M0D17LG,KAAA,IACE,YAAA,MACA,iBAAA,EACA,oBAAA,KACA,oBAAA,gB1D67LL,6B0Dx7LC,IAAA,IACE,YAAA,MACA,QAAA,IACA,iBAAA,EACA,oBAAA,KAEA,qB1D07LH,IAAA,I0Dz7LG,MAAA,MACE,WAAA,MACA,mBAAA,EACA,kBAAA,KACA,kBAAA,gB1D47LL,2B2DpjMC,MAAO,IACP,OAAA,M3DsjMD,QAAA,I2DnjMC,mBAAoB,EACpB,kBAAA,KAEA,U3DqjMD,SAAA,S2DljMG,gBACA,SAAA,SvD6KF,MAAA,KACK,SAAA,OJ04LN,sB2D/jMC,SAAU,S1D4lMV,QAAS,K0D9kML,mBAAA,IAAA,YAAA,K3DqjML,cAAA,IAAA,YAAA,K2D3hMC,WAAA,IAAA,YAAA,KvDmKK,4BAFL,0BAGQ,YAAA,EA3JA,qDA+GR,sBAEQ,mBAAA,kBAAA,IAAA,YJ86LP,cAAA,aAAA,IAAA,Y2DzjMG,WAAA,UAAA,IAAA,YvDmHJ,4BAAA,OACQ,oBAAA,OuDjHF,oBAAA,O3D4jML,YAAA,OI58LD,mCHs+LA,2BGr+LQ,KAAA,EuD5GF,kBAAA,sB3D6jML,UAAA,sBC2BD,kCADA,2BG5+LA,KAAA,EACQ,kBAAA,uBuDtGF,UAAA,uBArCN,6B3DomMD,gC2DpmMC,iC1D+nME,KAAM,E0DllMN,kBAAA,mB3D4jMH,UAAA,oBAGA,wB2D5mMD,sBAAA,sBAsDI,QAAA,MAEA,wB3D0jMH,KAAA,E2DtjMG,sB3DyjMH,sB2DrnMC,SAAU,SA+DR,IAAA,E3DyjMH,MAAA,KC0BD,sB0D/kMI,KAAA,KAnEJ,sBAuEI,KAAA,MAvEJ,2BA0EI,4B3DwjMH,KAAA,E2D/iMC,6BACA,KAAA,MAEA,8BACA,KAAA,KtC3FA,kBsC6FA,SAAA,SACA,IAAA,EACA,OAAA,EACA,KAAA,EACA,MAAA,I3DmjMD,UAAA,K2D9iMC,MAAA,KdnGE,WAAA,OACA,YAAA,EAAA,IAAA,IAAA,eACA,iBAAA,cAAA,OAAA,kBACA,QAAA,G7CqpMH,uB2DljMC,iBAAA,sEACE,iBAAA,iEACA,iBAAA,uFdxGA,iBAAA,kEACA,OAAA,+GACA,kBAAA,SACA,wBACA,MAAA,E7C6pMH,KAAA,K2DpjMC,iBAAA,sE1DglMA,iBAAiB,iE0D9kMf,iBAAA,uFACA,iBAAA,kEACA,OAAA,+GtCvHF,kBAAA,SsCyFF,wB3DslMC,wBC4BC,MAAO,KACP,gBAAiB,KACjB,OAAQ,kB0D7kMN,QAAA,EACA,QAAA,G3DwjMH,0C2DhmMD,2CA2CI,6BADA,6B1DklMF,SAAU,S0D7kMR,IAAA,IACA,QAAA,E3DqjMH,QAAA,a2DrmMC,WAAY,MAqDV,0CADA,6B3DsjMH,KAAA,I2D1mMC,YAAa,MA0DX,2CADA,6BAEA,MAAA,IACA,aAAA,MAME,6BADF,6B3DmjMH,MAAA,K2D9iMG,OAAA,KACE,YAAA,M3DgjML,YAAA,E2DriMC,oCACA,QAAA,QAEA,oCACA,QAAA,QAEA,qBACA,SAAA,SACA,OAAA,K3DwiMD,KAAA,I2DjjMC,QAAS,GAYP,MAAA,IACA,aAAA,EACA,YAAA,KACA,WAAA,OACA,WAAA,KAEA,wBACA,QAAA,aAWA,MAAA,KACA,OAAA,K3D8hMH,OAAA,I2D7jMC,YAAa,OAkCX,OAAA,QACA,iBAAA,OACA,iBAAA,cACA,OAAA,IAAA,MAAA,K3D8hMH,cAAA,K2DthMC,6BACA,MAAA,KACA,OAAA,KACA,OAAA,EACA,iBAAA,KAEA,kBACA,SAAA,SACA,MAAA,IACA,OAAA,K3DyhMD,KAAA,I2DxhMC,QAAA,GACE,YAAA,K3D0hMH,eAAA,K2Dj/LC,MAAO,KAhCP,WAAA,O1D8iMA,YAAa,EAAE,IAAI,IAAI,eAEzB,uB0D3iMM,YAAA,KAEA,oCACA,0C3DmhMH,2C2D3hMD,6BAAA,6BAYI,MAAA,K3DmhMH,OAAA,K2D/hMD,WAAA,M1D2jME,UAAW,KDxBZ,0C2D9gMD,6BACE,YAAA,MAEA,2C3DghMD,6B2D5gMD,aAAA,M3D+gMC,kBACF,MAAA,I4D7wMC,KAAA,I3DyyME,eAAgB,KAElB,qBACE,OAAQ,MAkBZ,qCADA,sCADA,mBADA,oBAXA,gBADA,iBAOA,uBADA,wBADA,iBADA,kBADA,wBADA,yBASA,mCADA,oC2DpzME,oBAAA,qBAAA,oBAAA,qB3D2zMF,WADA,YAOA,uBADA,wBADA,qBADA,sBADA,cADA,e2D/zMI,a3Dq0MJ,cDvBC,kB4D7yMG,mB3DqzMJ,WADA,YAwBE,QAAS,MACT,QAAS,IASX,qCADA,mBANA,gBAGA,uBADA,iBADA,wBAIA,mCDhBC,oB6D/0MC,oB5Dk2MF,W+B51MA,uBhCo0MC,qB4D5zMG,cChBF,aACA,kB5D+1MF,W+Br1ME,MAAO,KhCy0MR,cgCt0MC,QAAS,MACT,aAAA,KhCw0MD,YAAA,KgC/zMC,YhCk0MD,MAAA,gBgC/zMC,WhCk0MD,MAAA,egC/zMC,MhCk0MD,QAAA,e8Dz1MC,MACA,QAAA,gBAEA,WACA,WAAA,O9B8BF,WACE,KAAA,EAAA,EAAA,EhCg0MD,MAAA,YgCzzMC,YAAa,KACb,iBAAA,YhC2zMD,OAAA,E+D31MC,Q/D81MD,QAAA,eC4BD,OACE,SAAU,M+Dn4MV,chE42MD,MAAA,aC+BD,YADA,YADA,YADA,YAIE,QAAS,e+Dp5MT,kBhEs4MC,mBgEr4MD,yBhEi4MD,kB+Dl1MD,mBA6IA,yB9D4tMA,kBACA,mB8Dj3ME,yB9D62MF,kBACA,mBACA,yB+Dv5MY,QAAA,eACV,yBAAU,YhE04MT,QAAA,gBC4BD,iB+Dp6MU,QAAA,gBhE64MX,c+D51MG,QAAS,oB/Dg2MV,c+Dl2MC,c/Dm2MH,QAAA,sB+D91MG,yB/Dk2MD,kBACF,QAAA,iB+D91MG,yB/Dk2MD,mBACF,QAAA,kBgEh6MC,yBhEo6MC,yBgEn6MD,QAAA,wBACA,+CAAU,YhEw6MT,QAAA,gBC4BD,iB+Dl8MU,QAAA,gBhE26MX,c+Dr2MG,QAAS,oB/Dy2MV,c+D32MC,c/D42MH,QAAA,sB+Dv2MG,+C/D22MD,kBACF,QAAA,iB+Dv2MG,+C/D22MD,mBACF,QAAA,kBgE97MC,+ChEk8MC,yBgEj8MD,QAAA,wBACA,gDAAU,YhEs8MT,QAAA,gBC4BD,iB+Dh+MU,QAAA,gBhEy8MX,c+D92MG,QAAS,oB/Dk3MV,c+Dp3MC,c/Dq3MH,QAAA,sB+Dh3MG,gD/Do3MD,kBACF,QAAA,iB+Dh3MG,gD/Do3MD,mBACF,QAAA,kBgE59MC,gDhEg+MC,yBgE/9MD,QAAA,wBACA,0BAAU,YhEo+MT,QAAA,gBC4BD,iB+D9/MU,QAAA,gBhEu+MX,c+Dv3MG,QAAS,oB/D23MV,c+D73MC,c/D83MH,QAAA,sB+Dz3MG,0B/D63MD,kBACF,QAAA,iB+Dz3MG,0B/D63MD,mBACF,QAAA,kBgEl/MC,0BhEs/MC,yBACF,QAAA,wBgEv/MC,yBhE2/MC,WACF,QAAA,gBgE5/MC,+ChEggNC,WACF,QAAA,gBgEjgNC,gDhEqgNC,WACF,QAAA,gBAGA,0B+Dh3MC,WA4BE,QAAS,gBC5LX,eAAU,QAAA,eACV,aAAU,ehEyhNT,QAAA,gBC4BD,oB+DnjNU,QAAA,gBhE4hNX,iB+D93MG,QAAS,oBAMX,iB/D23MD,iB+Dt2MG,QAAS,sB/D22MZ,qB+D/3MC,QAAS,e/Dk4MV,a+D53MC,qBAcE,QAAS,iB/Dm3MZ,sB+Dh4MC,QAAS,e/Dm4MV,a+D73MC,sBAOE,QAAS,kB/D23MZ,4B+D53MC,QAAS,eCpLT,ahEojNC,4BACF,QAAA,wBC6BD,aACE,cACE,QAAS","sourcesContent":["/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */\n\n//\n// 1. Set default font family to sans-serif.\n// 2. Prevent iOS and IE text size adjust after device orientation change,\n// without disabling user zoom.\n//\n\nhtml {\n font-family: sans-serif; // 1\n -ms-text-size-adjust: 100%; // 2\n -webkit-text-size-adjust: 100%; // 2\n}\n\n//\n// Remove default margin.\n//\n\nbody {\n margin: 0;\n}\n\n// HTML5 display definitions\n// ==========================================================================\n\n//\n// Correct `block` display not defined for any HTML5 element in IE 8/9.\n// Correct `block` display not defined for `details` or `summary` in IE 10/11\n// and Firefox.\n// Correct `block` display not defined for `main` in IE 11.\n//\n\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nmenu,\nnav,\nsection,\nsummary {\n display: block;\n}\n\n//\n// 1. Correct `inline-block` display not defined in IE 8/9.\n// 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.\n//\n\naudio,\ncanvas,\nprogress,\nvideo {\n display: inline-block; // 1\n vertical-align: baseline; // 2\n}\n\n//\n// Prevent modern browsers from displaying `audio` without controls.\n// Remove excess height in iOS 5 devices.\n//\n\naudio:not([controls]) {\n display: none;\n height: 0;\n}\n\n//\n// Address `[hidden]` styling not present in IE 8/9/10.\n// Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22.\n//\n\n[hidden],\ntemplate {\n display: none;\n}\n\n// Links\n// ==========================================================================\n\n//\n// Remove the gray background color from active links in IE 10.\n//\n\na {\n background-color: transparent;\n}\n\n//\n// Improve readability of focused elements when they are also in an\n// active/hover state.\n//\n\na:active,\na:hover {\n outline: 0;\n}\n\n// Text-level semantics\n// ==========================================================================\n\n//\n// Address styling not present in IE 8/9/10/11, Safari, and Chrome.\n//\n\nabbr[title] {\n border-bottom: 1px dotted;\n}\n\n//\n// Address style set to `bolder` in Firefox 4+, Safari, and Chrome.\n//\n\nb,\nstrong {\n font-weight: bold;\n}\n\n//\n// Address styling not present in Safari and Chrome.\n//\n\ndfn {\n font-style: italic;\n}\n\n//\n// Address variable `h1` font-size and margin within `section` and `article`\n// contexts in Firefox 4+, Safari, and Chrome.\n//\n\nh1 {\n font-size: 2em;\n margin: 0.67em 0;\n}\n\n//\n// Address styling not present in IE 8/9.\n//\n\nmark {\n background: #ff0;\n color: #000;\n}\n\n//\n// Address inconsistent and variable font size in all browsers.\n//\n\nsmall {\n font-size: 80%;\n}\n\n//\n// Prevent `sub` and `sup` affecting `line-height` in all browsers.\n//\n\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\n\nsup {\n top: -0.5em;\n}\n\nsub {\n bottom: -0.25em;\n}\n\n// Embedded content\n// ==========================================================================\n\n//\n// Remove border when inside `a` element in IE 8/9/10.\n//\n\nimg {\n border: 0;\n}\n\n//\n// Correct overflow not hidden in IE 9/10/11.\n//\n\nsvg:not(:root) {\n overflow: hidden;\n}\n\n// Grouping content\n// ==========================================================================\n\n//\n// Address margin not present in IE 8/9 and Safari.\n//\n\nfigure {\n margin: 1em 40px;\n}\n\n//\n// Address differences between Firefox and other browsers.\n//\n\nhr {\n box-sizing: content-box;\n height: 0;\n}\n\n//\n// Contain overflow in all browsers.\n//\n\npre {\n overflow: auto;\n}\n\n//\n// Address odd `em`-unit font size rendering in all browsers.\n//\n\ncode,\nkbd,\npre,\nsamp {\n font-family: monospace, monospace;\n font-size: 1em;\n}\n\n// Forms\n// ==========================================================================\n\n//\n// Known limitation: by default, Chrome and Safari on OS X allow very limited\n// styling of `select`, unless a `border` property is set.\n//\n\n//\n// 1. Correct color not being inherited.\n// Known issue: affects color of disabled elements.\n// 2. Correct font properties not being inherited.\n// 3. Address margins set differently in Firefox 4+, Safari, and Chrome.\n//\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n color: inherit; // 1\n font: inherit; // 2\n margin: 0; // 3\n}\n\n//\n// Address `overflow` set to `hidden` in IE 8/9/10/11.\n//\n\nbutton {\n overflow: visible;\n}\n\n//\n// Address inconsistent `text-transform` inheritance for `button` and `select`.\n// All other form control elements do not inherit `text-transform` values.\n// Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.\n// Correct `select` style inheritance in Firefox.\n//\n\nbutton,\nselect {\n text-transform: none;\n}\n\n//\n// 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`\n// and `video` controls.\n// 2. Correct inability to style clickable `input` types in iOS.\n// 3. Improve usability and consistency of cursor style between image-type\n// `input` and others.\n//\n\nbutton,\nhtml input[type=\"button\"], // 1\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n -webkit-appearance: button; // 2\n cursor: pointer; // 3\n}\n\n//\n// Re-set default cursor for disabled elements.\n//\n\nbutton[disabled],\nhtml input[disabled] {\n cursor: default;\n}\n\n//\n// Remove inner padding and border in Firefox 4+.\n//\n\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n border: 0;\n padding: 0;\n}\n\n//\n// Address Firefox 4+ setting `line-height` on `input` using `!important` in\n// the UA stylesheet.\n//\n\ninput {\n line-height: normal;\n}\n\n//\n// It's recommended that you don't attempt to style these elements.\n// Firefox's implementation doesn't respect box-sizing, padding, or width.\n//\n// 1. Address box sizing set to `content-box` in IE 8/9/10.\n// 2. Remove excess padding in IE 8/9/10.\n//\n\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n box-sizing: border-box; // 1\n padding: 0; // 2\n}\n\n//\n// Fix the cursor style for Chrome's increment/decrement buttons. For certain\n// `font-size` values of the `input`, it causes the cursor style of the\n// decrement button to change from `default` to `text`.\n//\n\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n//\n// 1. Address `appearance` set to `searchfield` in Safari and Chrome.\n// 2. Address `box-sizing` set to `border-box` in Safari and Chrome.\n//\n\ninput[type=\"search\"] {\n -webkit-appearance: textfield; // 1\n box-sizing: content-box; //2\n}\n\n//\n// Remove inner padding and search cancel button in Safari and Chrome on OS X.\n// Safari (but not Chrome) clips the cancel button when the search input has\n// padding (and `textfield` appearance).\n//\n\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n//\n// Define consistent border, margin, and padding.\n//\n\nfieldset {\n border: 1px solid #c0c0c0;\n margin: 0 2px;\n padding: 0.35em 0.625em 0.75em;\n}\n\n//\n// 1. Correct `color` not being inherited in IE 8/9/10/11.\n// 2. Remove padding so people aren't caught out if they zero out fieldsets.\n//\n\nlegend {\n border: 0; // 1\n padding: 0; // 2\n}\n\n//\n// Remove default vertical scrollbar in IE 8/9/10/11.\n//\n\ntextarea {\n overflow: auto;\n}\n\n//\n// Don't inherit the `font-weight` (applied by a rule above).\n// NOTE: the default cannot safely be changed in Chrome and Safari on OS X.\n//\n\noptgroup {\n font-weight: bold;\n}\n\n// Tables\n// ==========================================================================\n\n//\n// Remove most spacing between table cells.\n//\n\ntable {\n border-collapse: collapse;\n border-spacing: 0;\n}\n\ntd,\nth {\n padding: 0;\n}\n","/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */\n\n// ==========================================================================\n// Print styles.\n// Inlined to avoid the additional HTTP request: h5bp.com/r\n// ==========================================================================\n\n@media print {\n *,\n *:before,\n *:after {\n background: transparent !important;\n color: #000 !important; // Black prints faster: h5bp.com/s\n box-shadow: none !important;\n text-shadow: none !important;\n }\n\n a,\n a:visited {\n text-decoration: underline;\n }\n\n a[href]:after {\n content: \" (\" attr(href) \")\";\n }\n\n abbr[title]:after {\n content: \" (\" attr(title) \")\";\n }\n\n // Don't show links that are fragment identifiers,\n // or use the `javascript:` pseudo protocol\n a[href^=\"#\"]:after,\n a[href^=\"javascript:\"]:after {\n content: \"\";\n }\n\n pre,\n blockquote {\n border: 1px solid #999;\n page-break-inside: avoid;\n }\n\n thead {\n display: table-header-group; // h5bp.com/t\n }\n\n tr,\n img {\n page-break-inside: avoid;\n }\n\n img {\n max-width: 100% !important;\n }\n\n p,\n h2,\n h3 {\n orphans: 3;\n widows: 3;\n }\n\n h2,\n h3 {\n page-break-after: avoid;\n }\n\n // Bootstrap specific changes start\n\n // Bootstrap components\n .navbar {\n display: none;\n }\n .btn,\n .dropup > .btn {\n > .caret {\n border-top-color: #000 !important;\n }\n }\n .label {\n border: 1px solid #000;\n }\n\n .table {\n border-collapse: collapse !important;\n\n td,\n th {\n background-color: #fff !important;\n }\n }\n .table-bordered {\n th,\n td {\n border: 1px solid #ddd !important;\n }\n }\n\n // Bootstrap specific changes end\n}\n","/*!\n * Bootstrap v3.3.7 (http://getbootstrap.com)\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */\nhtml {\n font-family: sans-serif;\n -ms-text-size-adjust: 100%;\n -webkit-text-size-adjust: 100%;\n}\nbody {\n margin: 0;\n}\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nmenu,\nnav,\nsection,\nsummary {\n display: block;\n}\naudio,\ncanvas,\nprogress,\nvideo {\n display: inline-block;\n vertical-align: baseline;\n}\naudio:not([controls]) {\n display: none;\n height: 0;\n}\n[hidden],\ntemplate {\n display: none;\n}\na {\n background-color: transparent;\n}\na:active,\na:hover {\n outline: 0;\n}\nabbr[title] {\n border-bottom: 1px dotted;\n}\nb,\nstrong {\n font-weight: bold;\n}\ndfn {\n font-style: italic;\n}\nh1 {\n font-size: 2em;\n margin: 0.67em 0;\n}\nmark {\n background: #ff0;\n color: #000;\n}\nsmall {\n font-size: 80%;\n}\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\nsup {\n top: -0.5em;\n}\nsub {\n bottom: -0.25em;\n}\nimg {\n border: 0;\n}\nsvg:not(:root) {\n overflow: hidden;\n}\nfigure {\n margin: 1em 40px;\n}\nhr {\n box-sizing: content-box;\n height: 0;\n}\npre {\n overflow: auto;\n}\ncode,\nkbd,\npre,\nsamp {\n font-family: monospace, monospace;\n font-size: 1em;\n}\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n color: inherit;\n font: inherit;\n margin: 0;\n}\nbutton {\n overflow: visible;\n}\nbutton,\nselect {\n text-transform: none;\n}\nbutton,\nhtml input[type=\"button\"],\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n -webkit-appearance: button;\n cursor: pointer;\n}\nbutton[disabled],\nhtml input[disabled] {\n cursor: default;\n}\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n border: 0;\n padding: 0;\n}\ninput {\n line-height: normal;\n}\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n box-sizing: border-box;\n padding: 0;\n}\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\ninput[type=\"search\"] {\n -webkit-appearance: textfield;\n box-sizing: content-box;\n}\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\nfieldset {\n border: 1px solid #c0c0c0;\n margin: 0 2px;\n padding: 0.35em 0.625em 0.75em;\n}\nlegend {\n border: 0;\n padding: 0;\n}\ntextarea {\n overflow: auto;\n}\noptgroup {\n font-weight: bold;\n}\ntable {\n border-collapse: collapse;\n border-spacing: 0;\n}\ntd,\nth {\n padding: 0;\n}\n/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */\n@media print {\n *,\n *:before,\n *:after {\n background: transparent !important;\n color: #000 !important;\n box-shadow: none !important;\n text-shadow: none !important;\n }\n a,\n a:visited {\n text-decoration: underline;\n }\n a[href]:after {\n content: \" (\" attr(href) \")\";\n }\n abbr[title]:after {\n content: \" (\" attr(title) \")\";\n }\n a[href^=\"#\"]:after,\n a[href^=\"javascript:\"]:after {\n content: \"\";\n }\n pre,\n blockquote {\n border: 1px solid #999;\n page-break-inside: avoid;\n }\n thead {\n display: table-header-group;\n }\n tr,\n img {\n page-break-inside: avoid;\n }\n img {\n max-width: 100% !important;\n }\n p,\n h2,\n h3 {\n orphans: 3;\n widows: 3;\n }\n h2,\n h3 {\n page-break-after: avoid;\n }\n .navbar {\n display: none;\n }\n .btn > .caret,\n .dropup > .btn > .caret {\n border-top-color: #000 !important;\n }\n .label {\n border: 1px solid #000;\n }\n .table {\n border-collapse: collapse !important;\n }\n .table td,\n .table th {\n background-color: #fff !important;\n }\n .table-bordered th,\n .table-bordered td {\n border: 1px solid #ddd !important;\n }\n}\n@font-face {\n font-family: 'Glyphicons Halflings';\n src: url('../fonts/glyphicons-halflings-regular.eot');\n src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');\n}\n.glyphicon {\n position: relative;\n top: 1px;\n display: inline-block;\n font-family: 'Glyphicons Halflings';\n font-style: normal;\n font-weight: normal;\n line-height: 1;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n.glyphicon-asterisk:before {\n content: \"\\002a\";\n}\n.glyphicon-plus:before {\n content: \"\\002b\";\n}\n.glyphicon-euro:before,\n.glyphicon-eur:before {\n content: \"\\20ac\";\n}\n.glyphicon-minus:before {\n content: \"\\2212\";\n}\n.glyphicon-cloud:before {\n content: \"\\2601\";\n}\n.glyphicon-envelope:before {\n content: \"\\2709\";\n}\n.glyphicon-pencil:before {\n content: \"\\270f\";\n}\n.glyphicon-glass:before {\n content: \"\\e001\";\n}\n.glyphicon-music:before {\n content: \"\\e002\";\n}\n.glyphicon-search:before {\n content: \"\\e003\";\n}\n.glyphicon-heart:before {\n content: \"\\e005\";\n}\n.glyphicon-star:before {\n content: \"\\e006\";\n}\n.glyphicon-star-empty:before {\n content: \"\\e007\";\n}\n.glyphicon-user:before {\n content: \"\\e008\";\n}\n.glyphicon-film:before {\n content: \"\\e009\";\n}\n.glyphicon-th-large:before {\n content: \"\\e010\";\n}\n.glyphicon-th:before {\n content: \"\\e011\";\n}\n.glyphicon-th-list:before {\n content: \"\\e012\";\n}\n.glyphicon-ok:before {\n content: \"\\e013\";\n}\n.glyphicon-remove:before {\n content: \"\\e014\";\n}\n.glyphicon-zoom-in:before {\n content: \"\\e015\";\n}\n.glyphicon-zoom-out:before {\n content: \"\\e016\";\n}\n.glyphicon-off:before {\n content: \"\\e017\";\n}\n.glyphicon-signal:before {\n content: \"\\e018\";\n}\n.glyphicon-cog:before {\n content: \"\\e019\";\n}\n.glyphicon-trash:before {\n content: \"\\e020\";\n}\n.glyphicon-home:before {\n content: \"\\e021\";\n}\n.glyphicon-file:before {\n content: \"\\e022\";\n}\n.glyphicon-time:before {\n content: \"\\e023\";\n}\n.glyphicon-road:before {\n content: \"\\e024\";\n}\n.glyphicon-download-alt:before {\n content: \"\\e025\";\n}\n.glyphicon-download:before {\n content: \"\\e026\";\n}\n.glyphicon-upload:before {\n content: \"\\e027\";\n}\n.glyphicon-inbox:before {\n content: \"\\e028\";\n}\n.glyphicon-play-circle:before {\n content: \"\\e029\";\n}\n.glyphicon-repeat:before {\n content: \"\\e030\";\n}\n.glyphicon-refresh:before {\n content: \"\\e031\";\n}\n.glyphicon-list-alt:before {\n content: \"\\e032\";\n}\n.glyphicon-lock:before {\n content: \"\\e033\";\n}\n.glyphicon-flag:before {\n content: \"\\e034\";\n}\n.glyphicon-headphones:before {\n content: \"\\e035\";\n}\n.glyphicon-volume-off:before {\n content: \"\\e036\";\n}\n.glyphicon-volume-down:before {\n content: \"\\e037\";\n}\n.glyphicon-volume-up:before {\n content: \"\\e038\";\n}\n.glyphicon-qrcode:before {\n content: \"\\e039\";\n}\n.glyphicon-barcode:before {\n content: \"\\e040\";\n}\n.glyphicon-tag:before {\n content: \"\\e041\";\n}\n.glyphicon-tags:before {\n content: \"\\e042\";\n}\n.glyphicon-book:before {\n content: \"\\e043\";\n}\n.glyphicon-bookmark:before {\n content: \"\\e044\";\n}\n.glyphicon-print:before {\n content: \"\\e045\";\n}\n.glyphicon-camera:before {\n content: \"\\e046\";\n}\n.glyphicon-font:before {\n content: \"\\e047\";\n}\n.glyphicon-bold:before {\n content: \"\\e048\";\n}\n.glyphicon-italic:before {\n content: \"\\e049\";\n}\n.glyphicon-text-height:before {\n content: \"\\e050\";\n}\n.glyphicon-text-width:before {\n content: \"\\e051\";\n}\n.glyphicon-align-left:before {\n content: \"\\e052\";\n}\n.glyphicon-align-center:before {\n content: \"\\e053\";\n}\n.glyphicon-align-right:before {\n content: \"\\e054\";\n}\n.glyphicon-align-justify:before {\n content: \"\\e055\";\n}\n.glyphicon-list:before {\n content: \"\\e056\";\n}\n.glyphicon-indent-left:before {\n content: \"\\e057\";\n}\n.glyphicon-indent-right:before {\n content: \"\\e058\";\n}\n.glyphicon-facetime-video:before {\n content: \"\\e059\";\n}\n.glyphicon-picture:before {\n content: \"\\e060\";\n}\n.glyphicon-map-marker:before {\n content: \"\\e062\";\n}\n.glyphicon-adjust:before {\n content: \"\\e063\";\n}\n.glyphicon-tint:before {\n content: \"\\e064\";\n}\n.glyphicon-edit:before {\n content: \"\\e065\";\n}\n.glyphicon-share:before {\n content: \"\\e066\";\n}\n.glyphicon-check:before {\n content: \"\\e067\";\n}\n.glyphicon-move:before {\n content: \"\\e068\";\n}\n.glyphicon-step-backward:before {\n content: \"\\e069\";\n}\n.glyphicon-fast-backward:before {\n content: \"\\e070\";\n}\n.glyphicon-backward:before {\n content: \"\\e071\";\n}\n.glyphicon-play:before {\n content: \"\\e072\";\n}\n.glyphicon-pause:before {\n content: \"\\e073\";\n}\n.glyphicon-stop:before {\n content: \"\\e074\";\n}\n.glyphicon-forward:before {\n content: \"\\e075\";\n}\n.glyphicon-fast-forward:before {\n content: \"\\e076\";\n}\n.glyphicon-step-forward:before {\n content: \"\\e077\";\n}\n.glyphicon-eject:before {\n content: \"\\e078\";\n}\n.glyphicon-chevron-left:before {\n content: \"\\e079\";\n}\n.glyphicon-chevron-right:before {\n content: \"\\e080\";\n}\n.glyphicon-plus-sign:before {\n content: \"\\e081\";\n}\n.glyphicon-minus-sign:before {\n content: \"\\e082\";\n}\n.glyphicon-remove-sign:before {\n content: \"\\e083\";\n}\n.glyphicon-ok-sign:before {\n content: \"\\e084\";\n}\n.glyphicon-question-sign:before {\n content: \"\\e085\";\n}\n.glyphicon-info-sign:before {\n content: \"\\e086\";\n}\n.glyphicon-screenshot:before {\n content: \"\\e087\";\n}\n.glyphicon-remove-circle:before {\n content: \"\\e088\";\n}\n.glyphicon-ok-circle:before {\n content: \"\\e089\";\n}\n.glyphicon-ban-circle:before {\n content: \"\\e090\";\n}\n.glyphicon-arrow-left:before {\n content: \"\\e091\";\n}\n.glyphicon-arrow-right:before {\n content: \"\\e092\";\n}\n.glyphicon-arrow-up:before {\n content: \"\\e093\";\n}\n.glyphicon-arrow-down:before {\n content: \"\\e094\";\n}\n.glyphicon-share-alt:before {\n content: \"\\e095\";\n}\n.glyphicon-resize-full:before {\n content: \"\\e096\";\n}\n.glyphicon-resize-small:before {\n content: \"\\e097\";\n}\n.glyphicon-exclamation-sign:before {\n content: \"\\e101\";\n}\n.glyphicon-gift:before {\n content: \"\\e102\";\n}\n.glyphicon-leaf:before {\n content: \"\\e103\";\n}\n.glyphicon-fire:before {\n content: \"\\e104\";\n}\n.glyphicon-eye-open:before {\n content: \"\\e105\";\n}\n.glyphicon-eye-close:before {\n content: \"\\e106\";\n}\n.glyphicon-warning-sign:before {\n content: \"\\e107\";\n}\n.glyphicon-plane:before {\n content: \"\\e108\";\n}\n.glyphicon-calendar:before {\n content: \"\\e109\";\n}\n.glyphicon-random:before {\n content: \"\\e110\";\n}\n.glyphicon-comment:before {\n content: \"\\e111\";\n}\n.glyphicon-magnet:before {\n content: \"\\e112\";\n}\n.glyphicon-chevron-up:before {\n content: \"\\e113\";\n}\n.glyphicon-chevron-down:before {\n content: \"\\e114\";\n}\n.glyphicon-retweet:before {\n content: \"\\e115\";\n}\n.glyphicon-shopping-cart:before {\n content: \"\\e116\";\n}\n.glyphicon-folder-close:before {\n content: \"\\e117\";\n}\n.glyphicon-folder-open:before {\n content: \"\\e118\";\n}\n.glyphicon-resize-vertical:before {\n content: \"\\e119\";\n}\n.glyphicon-resize-horizontal:before {\n content: \"\\e120\";\n}\n.glyphicon-hdd:before {\n content: \"\\e121\";\n}\n.glyphicon-bullhorn:before {\n content: \"\\e122\";\n}\n.glyphicon-bell:before {\n content: \"\\e123\";\n}\n.glyphicon-certificate:before {\n content: \"\\e124\";\n}\n.glyphicon-thumbs-up:before {\n content: \"\\e125\";\n}\n.glyphicon-thumbs-down:before {\n content: \"\\e126\";\n}\n.glyphicon-hand-right:before {\n content: \"\\e127\";\n}\n.glyphicon-hand-left:before {\n content: \"\\e128\";\n}\n.glyphicon-hand-up:before {\n content: \"\\e129\";\n}\n.glyphicon-hand-down:before {\n content: \"\\e130\";\n}\n.glyphicon-circle-arrow-right:before {\n content: \"\\e131\";\n}\n.glyphicon-circle-arrow-left:before {\n content: \"\\e132\";\n}\n.glyphicon-circle-arrow-up:before {\n content: \"\\e133\";\n}\n.glyphicon-circle-arrow-down:before {\n content: \"\\e134\";\n}\n.glyphicon-globe:before {\n content: \"\\e135\";\n}\n.glyphicon-wrench:before {\n content: \"\\e136\";\n}\n.glyphicon-tasks:before {\n content: \"\\e137\";\n}\n.glyphicon-filter:before {\n content: \"\\e138\";\n}\n.glyphicon-briefcase:before {\n content: \"\\e139\";\n}\n.glyphicon-fullscreen:before {\n content: \"\\e140\";\n}\n.glyphicon-dashboard:before {\n content: \"\\e141\";\n}\n.glyphicon-paperclip:before {\n content: \"\\e142\";\n}\n.glyphicon-heart-empty:before {\n content: \"\\e143\";\n}\n.glyphicon-link:before {\n content: \"\\e144\";\n}\n.glyphicon-phone:before {\n content: \"\\e145\";\n}\n.glyphicon-pushpin:before {\n content: \"\\e146\";\n}\n.glyphicon-usd:before {\n content: \"\\e148\";\n}\n.glyphicon-gbp:before {\n content: \"\\e149\";\n}\n.glyphicon-sort:before {\n content: \"\\e150\";\n}\n.glyphicon-sort-by-alphabet:before {\n content: \"\\e151\";\n}\n.glyphicon-sort-by-alphabet-alt:before {\n content: \"\\e152\";\n}\n.glyphicon-sort-by-order:before {\n content: \"\\e153\";\n}\n.glyphicon-sort-by-order-alt:before {\n content: \"\\e154\";\n}\n.glyphicon-sort-by-attributes:before {\n content: \"\\e155\";\n}\n.glyphicon-sort-by-attributes-alt:before {\n content: \"\\e156\";\n}\n.glyphicon-unchecked:before {\n content: \"\\e157\";\n}\n.glyphicon-expand:before {\n content: \"\\e158\";\n}\n.glyphicon-collapse-down:before {\n content: \"\\e159\";\n}\n.glyphicon-collapse-up:before {\n content: \"\\e160\";\n}\n.glyphicon-log-in:before {\n content: \"\\e161\";\n}\n.glyphicon-flash:before {\n content: \"\\e162\";\n}\n.glyphicon-log-out:before {\n content: \"\\e163\";\n}\n.glyphicon-new-window:before {\n content: \"\\e164\";\n}\n.glyphicon-record:before {\n content: \"\\e165\";\n}\n.glyphicon-save:before {\n content: \"\\e166\";\n}\n.glyphicon-open:before {\n content: \"\\e167\";\n}\n.glyphicon-saved:before {\n content: \"\\e168\";\n}\n.glyphicon-import:before {\n content: \"\\e169\";\n}\n.glyphicon-export:before {\n content: \"\\e170\";\n}\n.glyphicon-send:before {\n content: \"\\e171\";\n}\n.glyphicon-floppy-disk:before {\n content: \"\\e172\";\n}\n.glyphicon-floppy-saved:before {\n content: \"\\e173\";\n}\n.glyphicon-floppy-remove:before {\n content: \"\\e174\";\n}\n.glyphicon-floppy-save:before {\n content: \"\\e175\";\n}\n.glyphicon-floppy-open:before {\n content: \"\\e176\";\n}\n.glyphicon-credit-card:before {\n content: \"\\e177\";\n}\n.glyphicon-transfer:before {\n content: \"\\e178\";\n}\n.glyphicon-cutlery:before {\n content: \"\\e179\";\n}\n.glyphicon-header:before {\n content: \"\\e180\";\n}\n.glyphicon-compressed:before {\n content: \"\\e181\";\n}\n.glyphicon-earphone:before {\n content: \"\\e182\";\n}\n.glyphicon-phone-alt:before {\n content: \"\\e183\";\n}\n.glyphicon-tower:before {\n content: \"\\e184\";\n}\n.glyphicon-stats:before {\n content: \"\\e185\";\n}\n.glyphicon-sd-video:before {\n content: \"\\e186\";\n}\n.glyphicon-hd-video:before {\n content: \"\\e187\";\n}\n.glyphicon-subtitles:before {\n content: \"\\e188\";\n}\n.glyphicon-sound-stereo:before {\n content: \"\\e189\";\n}\n.glyphicon-sound-dolby:before {\n content: \"\\e190\";\n}\n.glyphicon-sound-5-1:before {\n content: \"\\e191\";\n}\n.glyphicon-sound-6-1:before {\n content: \"\\e192\";\n}\n.glyphicon-sound-7-1:before {\n content: \"\\e193\";\n}\n.glyphicon-copyright-mark:before {\n content: \"\\e194\";\n}\n.glyphicon-registration-mark:before {\n content: \"\\e195\";\n}\n.glyphicon-cloud-download:before {\n content: \"\\e197\";\n}\n.glyphicon-cloud-upload:before {\n content: \"\\e198\";\n}\n.glyphicon-tree-conifer:before {\n content: \"\\e199\";\n}\n.glyphicon-tree-deciduous:before {\n content: \"\\e200\";\n}\n.glyphicon-cd:before {\n content: \"\\e201\";\n}\n.glyphicon-save-file:before {\n content: \"\\e202\";\n}\n.glyphicon-open-file:before {\n content: \"\\e203\";\n}\n.glyphicon-level-up:before {\n content: \"\\e204\";\n}\n.glyphicon-copy:before {\n content: \"\\e205\";\n}\n.glyphicon-paste:before {\n content: \"\\e206\";\n}\n.glyphicon-alert:before {\n content: \"\\e209\";\n}\n.glyphicon-equalizer:before {\n content: \"\\e210\";\n}\n.glyphicon-king:before {\n content: \"\\e211\";\n}\n.glyphicon-queen:before {\n content: \"\\e212\";\n}\n.glyphicon-pawn:before {\n content: \"\\e213\";\n}\n.glyphicon-bishop:before {\n content: \"\\e214\";\n}\n.glyphicon-knight:before {\n content: \"\\e215\";\n}\n.glyphicon-baby-formula:before {\n content: \"\\e216\";\n}\n.glyphicon-tent:before {\n content: \"\\26fa\";\n}\n.glyphicon-blackboard:before {\n content: \"\\e218\";\n}\n.glyphicon-bed:before {\n content: \"\\e219\";\n}\n.glyphicon-apple:before {\n content: \"\\f8ff\";\n}\n.glyphicon-erase:before {\n content: \"\\e221\";\n}\n.glyphicon-hourglass:before {\n content: \"\\231b\";\n}\n.glyphicon-lamp:before {\n content: \"\\e223\";\n}\n.glyphicon-duplicate:before {\n content: \"\\e224\";\n}\n.glyphicon-piggy-bank:before {\n content: \"\\e225\";\n}\n.glyphicon-scissors:before {\n content: \"\\e226\";\n}\n.glyphicon-bitcoin:before {\n content: \"\\e227\";\n}\n.glyphicon-btc:before {\n content: \"\\e227\";\n}\n.glyphicon-xbt:before {\n content: \"\\e227\";\n}\n.glyphicon-yen:before {\n content: \"\\00a5\";\n}\n.glyphicon-jpy:before {\n content: \"\\00a5\";\n}\n.glyphicon-ruble:before {\n content: \"\\20bd\";\n}\n.glyphicon-rub:before {\n content: \"\\20bd\";\n}\n.glyphicon-scale:before {\n content: \"\\e230\";\n}\n.glyphicon-ice-lolly:before {\n content: \"\\e231\";\n}\n.glyphicon-ice-lolly-tasted:before {\n content: \"\\e232\";\n}\n.glyphicon-education:before {\n content: \"\\e233\";\n}\n.glyphicon-option-horizontal:before {\n content: \"\\e234\";\n}\n.glyphicon-option-vertical:before {\n content: \"\\e235\";\n}\n.glyphicon-menu-hamburger:before {\n content: \"\\e236\";\n}\n.glyphicon-modal-window:before {\n content: \"\\e237\";\n}\n.glyphicon-oil:before {\n content: \"\\e238\";\n}\n.glyphicon-grain:before {\n content: \"\\e239\";\n}\n.glyphicon-sunglasses:before {\n content: \"\\e240\";\n}\n.glyphicon-text-size:before {\n content: \"\\e241\";\n}\n.glyphicon-text-color:before {\n content: \"\\e242\";\n}\n.glyphicon-text-background:before {\n content: \"\\e243\";\n}\n.glyphicon-object-align-top:before {\n content: \"\\e244\";\n}\n.glyphicon-object-align-bottom:before {\n content: \"\\e245\";\n}\n.glyphicon-object-align-horizontal:before {\n content: \"\\e246\";\n}\n.glyphicon-object-align-left:before {\n content: \"\\e247\";\n}\n.glyphicon-object-align-vertical:before {\n content: \"\\e248\";\n}\n.glyphicon-object-align-right:before {\n content: \"\\e249\";\n}\n.glyphicon-triangle-right:before {\n content: \"\\e250\";\n}\n.glyphicon-triangle-left:before {\n content: \"\\e251\";\n}\n.glyphicon-triangle-bottom:before {\n content: \"\\e252\";\n}\n.glyphicon-triangle-top:before {\n content: \"\\e253\";\n}\n.glyphicon-console:before {\n content: \"\\e254\";\n}\n.glyphicon-superscript:before {\n content: \"\\e255\";\n}\n.glyphicon-subscript:before {\n content: \"\\e256\";\n}\n.glyphicon-menu-left:before {\n content: \"\\e257\";\n}\n.glyphicon-menu-right:before {\n content: \"\\e258\";\n}\n.glyphicon-menu-down:before {\n content: \"\\e259\";\n}\n.glyphicon-menu-up:before {\n content: \"\\e260\";\n}\n* {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\n*:before,\n*:after {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\nhtml {\n font-size: 10px;\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\nbody {\n font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n font-size: 14px;\n line-height: 1.42857143;\n color: #333333;\n background-color: #fff;\n}\ninput,\nbutton,\nselect,\ntextarea {\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\na {\n color: #337ab7;\n text-decoration: none;\n}\na:hover,\na:focus {\n color: #23527c;\n text-decoration: underline;\n}\na:focus {\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\nfigure {\n margin: 0;\n}\nimg {\n vertical-align: middle;\n}\n.img-responsive,\n.thumbnail > img,\n.thumbnail a > img,\n.carousel-inner > .item > img,\n.carousel-inner > .item > a > img {\n display: block;\n max-width: 100%;\n height: auto;\n}\n.img-rounded {\n border-radius: 6px;\n}\n.img-thumbnail {\n padding: 4px;\n line-height: 1.42857143;\n background-color: #fff;\n border: 1px solid #ddd;\n border-radius: 4px;\n -webkit-transition: all 0.2s ease-in-out;\n -o-transition: all 0.2s ease-in-out;\n transition: all 0.2s ease-in-out;\n display: inline-block;\n max-width: 100%;\n height: auto;\n}\n.img-circle {\n border-radius: 50%;\n}\nhr {\n margin-top: 20px;\n margin-bottom: 20px;\n border: 0;\n border-top: 1px solid #eeeeee;\n}\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n border: 0;\n}\n.sr-only-focusable:active,\n.sr-only-focusable:focus {\n position: static;\n width: auto;\n height: auto;\n margin: 0;\n overflow: visible;\n clip: auto;\n}\n[role=\"button\"] {\n cursor: pointer;\n}\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\n.h1,\n.h2,\n.h3,\n.h4,\n.h5,\n.h6 {\n font-family: inherit;\n font-weight: 500;\n line-height: 1.1;\n color: inherit;\n}\nh1 small,\nh2 small,\nh3 small,\nh4 small,\nh5 small,\nh6 small,\n.h1 small,\n.h2 small,\n.h3 small,\n.h4 small,\n.h5 small,\n.h6 small,\nh1 .small,\nh2 .small,\nh3 .small,\nh4 .small,\nh5 .small,\nh6 .small,\n.h1 .small,\n.h2 .small,\n.h3 .small,\n.h4 .small,\n.h5 .small,\n.h6 .small {\n font-weight: normal;\n line-height: 1;\n color: #777777;\n}\nh1,\n.h1,\nh2,\n.h2,\nh3,\n.h3 {\n margin-top: 20px;\n margin-bottom: 10px;\n}\nh1 small,\n.h1 small,\nh2 small,\n.h2 small,\nh3 small,\n.h3 small,\nh1 .small,\n.h1 .small,\nh2 .small,\n.h2 .small,\nh3 .small,\n.h3 .small {\n font-size: 65%;\n}\nh4,\n.h4,\nh5,\n.h5,\nh6,\n.h6 {\n margin-top: 10px;\n margin-bottom: 10px;\n}\nh4 small,\n.h4 small,\nh5 small,\n.h5 small,\nh6 small,\n.h6 small,\nh4 .small,\n.h4 .small,\nh5 .small,\n.h5 .small,\nh6 .small,\n.h6 .small {\n font-size: 75%;\n}\nh1,\n.h1 {\n font-size: 36px;\n}\nh2,\n.h2 {\n font-size: 30px;\n}\nh3,\n.h3 {\n font-size: 24px;\n}\nh4,\n.h4 {\n font-size: 18px;\n}\nh5,\n.h5 {\n font-size: 14px;\n}\nh6,\n.h6 {\n font-size: 12px;\n}\np {\n margin: 0 0 10px;\n}\n.lead {\n margin-bottom: 20px;\n font-size: 16px;\n font-weight: 300;\n line-height: 1.4;\n}\n@media (min-width: 768px) {\n .lead {\n font-size: 21px;\n }\n}\nsmall,\n.small {\n font-size: 85%;\n}\nmark,\n.mark {\n background-color: #fcf8e3;\n padding: .2em;\n}\n.text-left {\n text-align: left;\n}\n.text-right {\n text-align: right;\n}\n.text-center {\n text-align: center;\n}\n.text-justify {\n text-align: justify;\n}\n.text-nowrap {\n white-space: nowrap;\n}\n.text-lowercase {\n text-transform: lowercase;\n}\n.text-uppercase {\n text-transform: uppercase;\n}\n.text-capitalize {\n text-transform: capitalize;\n}\n.text-muted {\n color: #777777;\n}\n.text-primary {\n color: #337ab7;\n}\na.text-primary:hover,\na.text-primary:focus {\n color: #286090;\n}\n.text-success {\n color: #3c763d;\n}\na.text-success:hover,\na.text-success:focus {\n color: #2b542c;\n}\n.text-info {\n color: #31708f;\n}\na.text-info:hover,\na.text-info:focus {\n color: #245269;\n}\n.text-warning {\n color: #8a6d3b;\n}\na.text-warning:hover,\na.text-warning:focus {\n color: #66512c;\n}\n.text-danger {\n color: #a94442;\n}\na.text-danger:hover,\na.text-danger:focus {\n color: #843534;\n}\n.bg-primary {\n color: #fff;\n background-color: #337ab7;\n}\na.bg-primary:hover,\na.bg-primary:focus {\n background-color: #286090;\n}\n.bg-success {\n background-color: #dff0d8;\n}\na.bg-success:hover,\na.bg-success:focus {\n background-color: #c1e2b3;\n}\n.bg-info {\n background-color: #d9edf7;\n}\na.bg-info:hover,\na.bg-info:focus {\n background-color: #afd9ee;\n}\n.bg-warning {\n background-color: #fcf8e3;\n}\na.bg-warning:hover,\na.bg-warning:focus {\n background-color: #f7ecb5;\n}\n.bg-danger {\n background-color: #f2dede;\n}\na.bg-danger:hover,\na.bg-danger:focus {\n background-color: #e4b9b9;\n}\n.page-header {\n padding-bottom: 9px;\n margin: 40px 0 20px;\n border-bottom: 1px solid #eeeeee;\n}\nul,\nol {\n margin-top: 0;\n margin-bottom: 10px;\n}\nul ul,\nol ul,\nul ol,\nol ol {\n margin-bottom: 0;\n}\n.list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n.list-inline {\n padding-left: 0;\n list-style: none;\n margin-left: -5px;\n}\n.list-inline > li {\n display: inline-block;\n padding-left: 5px;\n padding-right: 5px;\n}\ndl {\n margin-top: 0;\n margin-bottom: 20px;\n}\ndt,\ndd {\n line-height: 1.42857143;\n}\ndt {\n font-weight: bold;\n}\ndd {\n margin-left: 0;\n}\n@media (min-width: 768px) {\n .dl-horizontal dt {\n float: left;\n width: 160px;\n clear: left;\n text-align: right;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n .dl-horizontal dd {\n margin-left: 180px;\n }\n}\nabbr[title],\nabbr[data-original-title] {\n cursor: help;\n border-bottom: 1px dotted #777777;\n}\n.initialism {\n font-size: 90%;\n text-transform: uppercase;\n}\nblockquote {\n padding: 10px 20px;\n margin: 0 0 20px;\n font-size: 17.5px;\n border-left: 5px solid #eeeeee;\n}\nblockquote p:last-child,\nblockquote ul:last-child,\nblockquote ol:last-child {\n margin-bottom: 0;\n}\nblockquote footer,\nblockquote small,\nblockquote .small {\n display: block;\n font-size: 80%;\n line-height: 1.42857143;\n color: #777777;\n}\nblockquote footer:before,\nblockquote small:before,\nblockquote .small:before {\n content: '\\2014 \\00A0';\n}\n.blockquote-reverse,\nblockquote.pull-right {\n padding-right: 15px;\n padding-left: 0;\n border-right: 5px solid #eeeeee;\n border-left: 0;\n text-align: right;\n}\n.blockquote-reverse footer:before,\nblockquote.pull-right footer:before,\n.blockquote-reverse small:before,\nblockquote.pull-right small:before,\n.blockquote-reverse .small:before,\nblockquote.pull-right .small:before {\n content: '';\n}\n.blockquote-reverse footer:after,\nblockquote.pull-right footer:after,\n.blockquote-reverse small:after,\nblockquote.pull-right small:after,\n.blockquote-reverse .small:after,\nblockquote.pull-right .small:after {\n content: '\\00A0 \\2014';\n}\naddress {\n margin-bottom: 20px;\n font-style: normal;\n line-height: 1.42857143;\n}\ncode,\nkbd,\npre,\nsamp {\n font-family: Menlo, Monaco, Consolas, \"Courier New\", monospace;\n}\ncode {\n padding: 2px 4px;\n font-size: 90%;\n color: #c7254e;\n background-color: #f9f2f4;\n border-radius: 4px;\n}\nkbd {\n padding: 2px 4px;\n font-size: 90%;\n color: #fff;\n background-color: #333;\n border-radius: 3px;\n box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25);\n}\nkbd kbd {\n padding: 0;\n font-size: 100%;\n font-weight: bold;\n box-shadow: none;\n}\npre {\n display: block;\n padding: 9.5px;\n margin: 0 0 10px;\n font-size: 13px;\n line-height: 1.42857143;\n word-break: break-all;\n word-wrap: break-word;\n color: #333333;\n background-color: #f5f5f5;\n border: 1px solid #ccc;\n border-radius: 4px;\n}\npre code {\n padding: 0;\n font-size: inherit;\n color: inherit;\n white-space: pre-wrap;\n background-color: transparent;\n border-radius: 0;\n}\n.pre-scrollable {\n max-height: 340px;\n overflow-y: scroll;\n}\n.container {\n margin-right: auto;\n margin-left: auto;\n padding-left: 15px;\n padding-right: 15px;\n}\n@media (min-width: 768px) {\n .container {\n width: 750px;\n }\n}\n@media (min-width: 992px) {\n .container {\n width: 970px;\n }\n}\n@media (min-width: 1200px) {\n .container {\n width: 1170px;\n }\n}\n.container-fluid {\n margin-right: auto;\n margin-left: auto;\n padding-left: 15px;\n padding-right: 15px;\n}\n.row {\n margin-left: -15px;\n margin-right: -15px;\n}\n.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 {\n position: relative;\n min-height: 1px;\n padding-left: 15px;\n padding-right: 15px;\n}\n.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 {\n float: left;\n}\n.col-xs-12 {\n width: 100%;\n}\n.col-xs-11 {\n width: 91.66666667%;\n}\n.col-xs-10 {\n width: 83.33333333%;\n}\n.col-xs-9 {\n width: 75%;\n}\n.col-xs-8 {\n width: 66.66666667%;\n}\n.col-xs-7 {\n width: 58.33333333%;\n}\n.col-xs-6 {\n width: 50%;\n}\n.col-xs-5 {\n width: 41.66666667%;\n}\n.col-xs-4 {\n width: 33.33333333%;\n}\n.col-xs-3 {\n width: 25%;\n}\n.col-xs-2 {\n width: 16.66666667%;\n}\n.col-xs-1 {\n width: 8.33333333%;\n}\n.col-xs-pull-12 {\n right: 100%;\n}\n.col-xs-pull-11 {\n right: 91.66666667%;\n}\n.col-xs-pull-10 {\n right: 83.33333333%;\n}\n.col-xs-pull-9 {\n right: 75%;\n}\n.col-xs-pull-8 {\n right: 66.66666667%;\n}\n.col-xs-pull-7 {\n right: 58.33333333%;\n}\n.col-xs-pull-6 {\n right: 50%;\n}\n.col-xs-pull-5 {\n right: 41.66666667%;\n}\n.col-xs-pull-4 {\n right: 33.33333333%;\n}\n.col-xs-pull-3 {\n right: 25%;\n}\n.col-xs-pull-2 {\n right: 16.66666667%;\n}\n.col-xs-pull-1 {\n right: 8.33333333%;\n}\n.col-xs-pull-0 {\n right: auto;\n}\n.col-xs-push-12 {\n left: 100%;\n}\n.col-xs-push-11 {\n left: 91.66666667%;\n}\n.col-xs-push-10 {\n left: 83.33333333%;\n}\n.col-xs-push-9 {\n left: 75%;\n}\n.col-xs-push-8 {\n left: 66.66666667%;\n}\n.col-xs-push-7 {\n left: 58.33333333%;\n}\n.col-xs-push-6 {\n left: 50%;\n}\n.col-xs-push-5 {\n left: 41.66666667%;\n}\n.col-xs-push-4 {\n left: 33.33333333%;\n}\n.col-xs-push-3 {\n left: 25%;\n}\n.col-xs-push-2 {\n left: 16.66666667%;\n}\n.col-xs-push-1 {\n left: 8.33333333%;\n}\n.col-xs-push-0 {\n left: auto;\n}\n.col-xs-offset-12 {\n margin-left: 100%;\n}\n.col-xs-offset-11 {\n margin-left: 91.66666667%;\n}\n.col-xs-offset-10 {\n margin-left: 83.33333333%;\n}\n.col-xs-offset-9 {\n margin-left: 75%;\n}\n.col-xs-offset-8 {\n margin-left: 66.66666667%;\n}\n.col-xs-offset-7 {\n margin-left: 58.33333333%;\n}\n.col-xs-offset-6 {\n margin-left: 50%;\n}\n.col-xs-offset-5 {\n margin-left: 41.66666667%;\n}\n.col-xs-offset-4 {\n margin-left: 33.33333333%;\n}\n.col-xs-offset-3 {\n margin-left: 25%;\n}\n.col-xs-offset-2 {\n margin-left: 16.66666667%;\n}\n.col-xs-offset-1 {\n margin-left: 8.33333333%;\n}\n.col-xs-offset-0 {\n margin-left: 0%;\n}\n@media (min-width: 768px) {\n .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 {\n float: left;\n }\n .col-sm-12 {\n width: 100%;\n }\n .col-sm-11 {\n width: 91.66666667%;\n }\n .col-sm-10 {\n width: 83.33333333%;\n }\n .col-sm-9 {\n width: 75%;\n }\n .col-sm-8 {\n width: 66.66666667%;\n }\n .col-sm-7 {\n width: 58.33333333%;\n }\n .col-sm-6 {\n width: 50%;\n }\n .col-sm-5 {\n width: 41.66666667%;\n }\n .col-sm-4 {\n width: 33.33333333%;\n }\n .col-sm-3 {\n width: 25%;\n }\n .col-sm-2 {\n width: 16.66666667%;\n }\n .col-sm-1 {\n width: 8.33333333%;\n }\n .col-sm-pull-12 {\n right: 100%;\n }\n .col-sm-pull-11 {\n right: 91.66666667%;\n }\n .col-sm-pull-10 {\n right: 83.33333333%;\n }\n .col-sm-pull-9 {\n right: 75%;\n }\n .col-sm-pull-8 {\n right: 66.66666667%;\n }\n .col-sm-pull-7 {\n right: 58.33333333%;\n }\n .col-sm-pull-6 {\n right: 50%;\n }\n .col-sm-pull-5 {\n right: 41.66666667%;\n }\n .col-sm-pull-4 {\n right: 33.33333333%;\n }\n .col-sm-pull-3 {\n right: 25%;\n }\n .col-sm-pull-2 {\n right: 16.66666667%;\n }\n .col-sm-pull-1 {\n right: 8.33333333%;\n }\n .col-sm-pull-0 {\n right: auto;\n }\n .col-sm-push-12 {\n left: 100%;\n }\n .col-sm-push-11 {\n left: 91.66666667%;\n }\n .col-sm-push-10 {\n left: 83.33333333%;\n }\n .col-sm-push-9 {\n left: 75%;\n }\n .col-sm-push-8 {\n left: 66.66666667%;\n }\n .col-sm-push-7 {\n left: 58.33333333%;\n }\n .col-sm-push-6 {\n left: 50%;\n }\n .col-sm-push-5 {\n left: 41.66666667%;\n }\n .col-sm-push-4 {\n left: 33.33333333%;\n }\n .col-sm-push-3 {\n left: 25%;\n }\n .col-sm-push-2 {\n left: 16.66666667%;\n }\n .col-sm-push-1 {\n left: 8.33333333%;\n }\n .col-sm-push-0 {\n left: auto;\n }\n .col-sm-offset-12 {\n margin-left: 100%;\n }\n .col-sm-offset-11 {\n margin-left: 91.66666667%;\n }\n .col-sm-offset-10 {\n margin-left: 83.33333333%;\n }\n .col-sm-offset-9 {\n margin-left: 75%;\n }\n .col-sm-offset-8 {\n margin-left: 66.66666667%;\n }\n .col-sm-offset-7 {\n margin-left: 58.33333333%;\n }\n .col-sm-offset-6 {\n margin-left: 50%;\n }\n .col-sm-offset-5 {\n margin-left: 41.66666667%;\n }\n .col-sm-offset-4 {\n margin-left: 33.33333333%;\n }\n .col-sm-offset-3 {\n margin-left: 25%;\n }\n .col-sm-offset-2 {\n margin-left: 16.66666667%;\n }\n .col-sm-offset-1 {\n margin-left: 8.33333333%;\n }\n .col-sm-offset-0 {\n margin-left: 0%;\n }\n}\n@media (min-width: 992px) {\n .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 {\n float: left;\n }\n .col-md-12 {\n width: 100%;\n }\n .col-md-11 {\n width: 91.66666667%;\n }\n .col-md-10 {\n width: 83.33333333%;\n }\n .col-md-9 {\n width: 75%;\n }\n .col-md-8 {\n width: 66.66666667%;\n }\n .col-md-7 {\n width: 58.33333333%;\n }\n .col-md-6 {\n width: 50%;\n }\n .col-md-5 {\n width: 41.66666667%;\n }\n .col-md-4 {\n width: 33.33333333%;\n }\n .col-md-3 {\n width: 25%;\n }\n .col-md-2 {\n width: 16.66666667%;\n }\n .col-md-1 {\n width: 8.33333333%;\n }\n .col-md-pull-12 {\n right: 100%;\n }\n .col-md-pull-11 {\n right: 91.66666667%;\n }\n .col-md-pull-10 {\n right: 83.33333333%;\n }\n .col-md-pull-9 {\n right: 75%;\n }\n .col-md-pull-8 {\n right: 66.66666667%;\n }\n .col-md-pull-7 {\n right: 58.33333333%;\n }\n .col-md-pull-6 {\n right: 50%;\n }\n .col-md-pull-5 {\n right: 41.66666667%;\n }\n .col-md-pull-4 {\n right: 33.33333333%;\n }\n .col-md-pull-3 {\n right: 25%;\n }\n .col-md-pull-2 {\n right: 16.66666667%;\n }\n .col-md-pull-1 {\n right: 8.33333333%;\n }\n .col-md-pull-0 {\n right: auto;\n }\n .col-md-push-12 {\n left: 100%;\n }\n .col-md-push-11 {\n left: 91.66666667%;\n }\n .col-md-push-10 {\n left: 83.33333333%;\n }\n .col-md-push-9 {\n left: 75%;\n }\n .col-md-push-8 {\n left: 66.66666667%;\n }\n .col-md-push-7 {\n left: 58.33333333%;\n }\n .col-md-push-6 {\n left: 50%;\n }\n .col-md-push-5 {\n left: 41.66666667%;\n }\n .col-md-push-4 {\n left: 33.33333333%;\n }\n .col-md-push-3 {\n left: 25%;\n }\n .col-md-push-2 {\n left: 16.66666667%;\n }\n .col-md-push-1 {\n left: 8.33333333%;\n }\n .col-md-push-0 {\n left: auto;\n }\n .col-md-offset-12 {\n margin-left: 100%;\n }\n .col-md-offset-11 {\n margin-left: 91.66666667%;\n }\n .col-md-offset-10 {\n margin-left: 83.33333333%;\n }\n .col-md-offset-9 {\n margin-left: 75%;\n }\n .col-md-offset-8 {\n margin-left: 66.66666667%;\n }\n .col-md-offset-7 {\n margin-left: 58.33333333%;\n }\n .col-md-offset-6 {\n margin-left: 50%;\n }\n .col-md-offset-5 {\n margin-left: 41.66666667%;\n }\n .col-md-offset-4 {\n margin-left: 33.33333333%;\n }\n .col-md-offset-3 {\n margin-left: 25%;\n }\n .col-md-offset-2 {\n margin-left: 16.66666667%;\n }\n .col-md-offset-1 {\n margin-left: 8.33333333%;\n }\n .col-md-offset-0 {\n margin-left: 0%;\n }\n}\n@media (min-width: 1200px) {\n .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 {\n float: left;\n }\n .col-lg-12 {\n width: 100%;\n }\n .col-lg-11 {\n width: 91.66666667%;\n }\n .col-lg-10 {\n width: 83.33333333%;\n }\n .col-lg-9 {\n width: 75%;\n }\n .col-lg-8 {\n width: 66.66666667%;\n }\n .col-lg-7 {\n width: 58.33333333%;\n }\n .col-lg-6 {\n width: 50%;\n }\n .col-lg-5 {\n width: 41.66666667%;\n }\n .col-lg-4 {\n width: 33.33333333%;\n }\n .col-lg-3 {\n width: 25%;\n }\n .col-lg-2 {\n width: 16.66666667%;\n }\n .col-lg-1 {\n width: 8.33333333%;\n }\n .col-lg-pull-12 {\n right: 100%;\n }\n .col-lg-pull-11 {\n right: 91.66666667%;\n }\n .col-lg-pull-10 {\n right: 83.33333333%;\n }\n .col-lg-pull-9 {\n right: 75%;\n }\n .col-lg-pull-8 {\n right: 66.66666667%;\n }\n .col-lg-pull-7 {\n right: 58.33333333%;\n }\n .col-lg-pull-6 {\n right: 50%;\n }\n .col-lg-pull-5 {\n right: 41.66666667%;\n }\n .col-lg-pull-4 {\n right: 33.33333333%;\n }\n .col-lg-pull-3 {\n right: 25%;\n }\n .col-lg-pull-2 {\n right: 16.66666667%;\n }\n .col-lg-pull-1 {\n right: 8.33333333%;\n }\n .col-lg-pull-0 {\n right: auto;\n }\n .col-lg-push-12 {\n left: 100%;\n }\n .col-lg-push-11 {\n left: 91.66666667%;\n }\n .col-lg-push-10 {\n left: 83.33333333%;\n }\n .col-lg-push-9 {\n left: 75%;\n }\n .col-lg-push-8 {\n left: 66.66666667%;\n }\n .col-lg-push-7 {\n left: 58.33333333%;\n }\n .col-lg-push-6 {\n left: 50%;\n }\n .col-lg-push-5 {\n left: 41.66666667%;\n }\n .col-lg-push-4 {\n left: 33.33333333%;\n }\n .col-lg-push-3 {\n left: 25%;\n }\n .col-lg-push-2 {\n left: 16.66666667%;\n }\n .col-lg-push-1 {\n left: 8.33333333%;\n }\n .col-lg-push-0 {\n left: auto;\n }\n .col-lg-offset-12 {\n margin-left: 100%;\n }\n .col-lg-offset-11 {\n margin-left: 91.66666667%;\n }\n .col-lg-offset-10 {\n margin-left: 83.33333333%;\n }\n .col-lg-offset-9 {\n margin-left: 75%;\n }\n .col-lg-offset-8 {\n margin-left: 66.66666667%;\n }\n .col-lg-offset-7 {\n margin-left: 58.33333333%;\n }\n .col-lg-offset-6 {\n margin-left: 50%;\n }\n .col-lg-offset-5 {\n margin-left: 41.66666667%;\n }\n .col-lg-offset-4 {\n margin-left: 33.33333333%;\n }\n .col-lg-offset-3 {\n margin-left: 25%;\n }\n .col-lg-offset-2 {\n margin-left: 16.66666667%;\n }\n .col-lg-offset-1 {\n margin-left: 8.33333333%;\n }\n .col-lg-offset-0 {\n margin-left: 0%;\n }\n}\ntable {\n background-color: transparent;\n}\ncaption {\n padding-top: 8px;\n padding-bottom: 8px;\n color: #777777;\n text-align: left;\n}\nth {\n text-align: left;\n}\n.table {\n width: 100%;\n max-width: 100%;\n margin-bottom: 20px;\n}\n.table > thead > tr > th,\n.table > tbody > tr > th,\n.table > tfoot > tr > th,\n.table > thead > tr > td,\n.table > tbody > tr > td,\n.table > tfoot > tr > td {\n padding: 8px;\n line-height: 1.42857143;\n vertical-align: top;\n border-top: 1px solid #ddd;\n}\n.table > thead > tr > th {\n vertical-align: bottom;\n border-bottom: 2px solid #ddd;\n}\n.table > caption + thead > tr:first-child > th,\n.table > colgroup + thead > tr:first-child > th,\n.table > thead:first-child > tr:first-child > th,\n.table > caption + thead > tr:first-child > td,\n.table > colgroup + thead > tr:first-child > td,\n.table > thead:first-child > tr:first-child > td {\n border-top: 0;\n}\n.table > tbody + tbody {\n border-top: 2px solid #ddd;\n}\n.table .table {\n background-color: #fff;\n}\n.table-condensed > thead > tr > th,\n.table-condensed > tbody > tr > th,\n.table-condensed > tfoot > tr > th,\n.table-condensed > thead > tr > td,\n.table-condensed > tbody > tr > td,\n.table-condensed > tfoot > tr > td {\n padding: 5px;\n}\n.table-bordered {\n border: 1px solid #ddd;\n}\n.table-bordered > thead > tr > th,\n.table-bordered > tbody > tr > th,\n.table-bordered > tfoot > tr > th,\n.table-bordered > thead > tr > td,\n.table-bordered > tbody > tr > td,\n.table-bordered > tfoot > tr > td {\n border: 1px solid #ddd;\n}\n.table-bordered > thead > tr > th,\n.table-bordered > thead > tr > td {\n border-bottom-width: 2px;\n}\n.table-striped > tbody > tr:nth-of-type(odd) {\n background-color: #f9f9f9;\n}\n.table-hover > tbody > tr:hover {\n background-color: #f5f5f5;\n}\ntable col[class*=\"col-\"] {\n position: static;\n float: none;\n display: table-column;\n}\ntable td[class*=\"col-\"],\ntable th[class*=\"col-\"] {\n position: static;\n float: none;\n display: table-cell;\n}\n.table > thead > tr > td.active,\n.table > tbody > tr > td.active,\n.table > tfoot > tr > td.active,\n.table > thead > tr > th.active,\n.table > tbody > tr > th.active,\n.table > tfoot > tr > th.active,\n.table > thead > tr.active > td,\n.table > tbody > tr.active > td,\n.table > tfoot > tr.active > td,\n.table > thead > tr.active > th,\n.table > tbody > tr.active > th,\n.table > tfoot > tr.active > th {\n background-color: #f5f5f5;\n}\n.table-hover > tbody > tr > td.active:hover,\n.table-hover > tbody > tr > th.active:hover,\n.table-hover > tbody > tr.active:hover > td,\n.table-hover > tbody > tr:hover > .active,\n.table-hover > tbody > tr.active:hover > th {\n background-color: #e8e8e8;\n}\n.table > thead > tr > td.success,\n.table > tbody > tr > td.success,\n.table > tfoot > tr > td.success,\n.table > thead > tr > th.success,\n.table > tbody > tr > th.success,\n.table > tfoot > tr > th.success,\n.table > thead > tr.success > td,\n.table > tbody > tr.success > td,\n.table > tfoot > tr.success > td,\n.table > thead > tr.success > th,\n.table > tbody > tr.success > th,\n.table > tfoot > tr.success > th {\n background-color: #dff0d8;\n}\n.table-hover > tbody > tr > td.success:hover,\n.table-hover > tbody > tr > th.success:hover,\n.table-hover > tbody > tr.success:hover > td,\n.table-hover > tbody > tr:hover > .success,\n.table-hover > tbody > tr.success:hover > th {\n background-color: #d0e9c6;\n}\n.table > thead > tr > td.info,\n.table > tbody > tr > td.info,\n.table > tfoot > tr > td.info,\n.table > thead > tr > th.info,\n.table > tbody > tr > th.info,\n.table > tfoot > tr > th.info,\n.table > thead > tr.info > td,\n.table > tbody > tr.info > td,\n.table > tfoot > tr.info > td,\n.table > thead > tr.info > th,\n.table > tbody > tr.info > th,\n.table > tfoot > tr.info > th {\n background-color: #d9edf7;\n}\n.table-hover > tbody > tr > td.info:hover,\n.table-hover > tbody > tr > th.info:hover,\n.table-hover > tbody > tr.info:hover > td,\n.table-hover > tbody > tr:hover > .info,\n.table-hover > tbody > tr.info:hover > th {\n background-color: #c4e3f3;\n}\n.table > thead > tr > td.warning,\n.table > tbody > tr > td.warning,\n.table > tfoot > tr > td.warning,\n.table > thead > tr > th.warning,\n.table > tbody > tr > th.warning,\n.table > tfoot > tr > th.warning,\n.table > thead > tr.warning > td,\n.table > tbody > tr.warning > td,\n.table > tfoot > tr.warning > td,\n.table > thead > tr.warning > th,\n.table > tbody > tr.warning > th,\n.table > tfoot > tr.warning > th {\n background-color: #fcf8e3;\n}\n.table-hover > tbody > tr > td.warning:hover,\n.table-hover > tbody > tr > th.warning:hover,\n.table-hover > tbody > tr.warning:hover > td,\n.table-hover > tbody > tr:hover > .warning,\n.table-hover > tbody > tr.warning:hover > th {\n background-color: #faf2cc;\n}\n.table > thead > tr > td.danger,\n.table > tbody > tr > td.danger,\n.table > tfoot > tr > td.danger,\n.table > thead > tr > th.danger,\n.table > tbody > tr > th.danger,\n.table > tfoot > tr > th.danger,\n.table > thead > tr.danger > td,\n.table > tbody > tr.danger > td,\n.table > tfoot > tr.danger > td,\n.table > thead > tr.danger > th,\n.table > tbody > tr.danger > th,\n.table > tfoot > tr.danger > th {\n background-color: #f2dede;\n}\n.table-hover > tbody > tr > td.danger:hover,\n.table-hover > tbody > tr > th.danger:hover,\n.table-hover > tbody > tr.danger:hover > td,\n.table-hover > tbody > tr:hover > .danger,\n.table-hover > tbody > tr.danger:hover > th {\n background-color: #ebcccc;\n}\n.table-responsive {\n overflow-x: auto;\n min-height: 0.01%;\n}\n@media screen and (max-width: 767px) {\n .table-responsive {\n width: 100%;\n margin-bottom: 15px;\n overflow-y: hidden;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n border: 1px solid #ddd;\n }\n .table-responsive > .table {\n margin-bottom: 0;\n }\n .table-responsive > .table > thead > tr > th,\n .table-responsive > .table > tbody > tr > th,\n .table-responsive > .table > tfoot > tr > th,\n .table-responsive > .table > thead > tr > td,\n .table-responsive > .table > tbody > tr > td,\n .table-responsive > .table > tfoot > tr > td {\n white-space: nowrap;\n }\n .table-responsive > .table-bordered {\n border: 0;\n }\n .table-responsive > .table-bordered > thead > tr > th:first-child,\n .table-responsive > .table-bordered > tbody > tr > th:first-child,\n .table-responsive > .table-bordered > tfoot > tr > th:first-child,\n .table-responsive > .table-bordered > thead > tr > td:first-child,\n .table-responsive > .table-bordered > tbody > tr > td:first-child,\n .table-responsive > .table-bordered > tfoot > tr > td:first-child {\n border-left: 0;\n }\n .table-responsive > .table-bordered > thead > tr > th:last-child,\n .table-responsive > .table-bordered > tbody > tr > th:last-child,\n .table-responsive > .table-bordered > tfoot > tr > th:last-child,\n .table-responsive > .table-bordered > thead > tr > td:last-child,\n .table-responsive > .table-bordered > tbody > tr > td:last-child,\n .table-responsive > .table-bordered > tfoot > tr > td:last-child {\n border-right: 0;\n }\n .table-responsive > .table-bordered > tbody > tr:last-child > th,\n .table-responsive > .table-bordered > tfoot > tr:last-child > th,\n .table-responsive > .table-bordered > tbody > tr:last-child > td,\n .table-responsive > .table-bordered > tfoot > tr:last-child > td {\n border-bottom: 0;\n }\n}\nfieldset {\n padding: 0;\n margin: 0;\n border: 0;\n min-width: 0;\n}\nlegend {\n display: block;\n width: 100%;\n padding: 0;\n margin-bottom: 20px;\n font-size: 21px;\n line-height: inherit;\n color: #333333;\n border: 0;\n border-bottom: 1px solid #e5e5e5;\n}\nlabel {\n display: inline-block;\n max-width: 100%;\n margin-bottom: 5px;\n font-weight: bold;\n}\ninput[type=\"search\"] {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n margin: 4px 0 0;\n margin-top: 1px \\9;\n line-height: normal;\n}\ninput[type=\"file\"] {\n display: block;\n}\ninput[type=\"range\"] {\n display: block;\n width: 100%;\n}\nselect[multiple],\nselect[size] {\n height: auto;\n}\ninput[type=\"file\"]:focus,\ninput[type=\"radio\"]:focus,\ninput[type=\"checkbox\"]:focus {\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\noutput {\n display: block;\n padding-top: 7px;\n font-size: 14px;\n line-height: 1.42857143;\n color: #555555;\n}\n.form-control {\n display: block;\n width: 100%;\n height: 34px;\n padding: 6px 12px;\n font-size: 14px;\n line-height: 1.42857143;\n color: #555555;\n background-color: #fff;\n background-image: none;\n border: 1px solid #ccc;\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n -webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n}\n.form-control:focus {\n border-color: #66afe9;\n outline: 0;\n -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);\n box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);\n}\n.form-control::-moz-placeholder {\n color: #999;\n opacity: 1;\n}\n.form-control:-ms-input-placeholder {\n color: #999;\n}\n.form-control::-webkit-input-placeholder {\n color: #999;\n}\n.form-control::-ms-expand {\n border: 0;\n background-color: transparent;\n}\n.form-control[disabled],\n.form-control[readonly],\nfieldset[disabled] .form-control {\n background-color: #eeeeee;\n opacity: 1;\n}\n.form-control[disabled],\nfieldset[disabled] .form-control {\n cursor: not-allowed;\n}\ntextarea.form-control {\n height: auto;\n}\ninput[type=\"search\"] {\n -webkit-appearance: none;\n}\n@media screen and (-webkit-min-device-pixel-ratio: 0) {\n input[type=\"date\"].form-control,\n input[type=\"time\"].form-control,\n input[type=\"datetime-local\"].form-control,\n input[type=\"month\"].form-control {\n line-height: 34px;\n }\n input[type=\"date\"].input-sm,\n input[type=\"time\"].input-sm,\n input[type=\"datetime-local\"].input-sm,\n input[type=\"month\"].input-sm,\n .input-group-sm input[type=\"date\"],\n .input-group-sm input[type=\"time\"],\n .input-group-sm input[type=\"datetime-local\"],\n .input-group-sm input[type=\"month\"] {\n line-height: 30px;\n }\n input[type=\"date\"].input-lg,\n input[type=\"time\"].input-lg,\n input[type=\"datetime-local\"].input-lg,\n input[type=\"month\"].input-lg,\n .input-group-lg input[type=\"date\"],\n .input-group-lg input[type=\"time\"],\n .input-group-lg input[type=\"datetime-local\"],\n .input-group-lg input[type=\"month\"] {\n line-height: 46px;\n }\n}\n.form-group {\n margin-bottom: 15px;\n}\n.radio,\n.checkbox {\n position: relative;\n display: block;\n margin-top: 10px;\n margin-bottom: 10px;\n}\n.radio label,\n.checkbox label {\n min-height: 20px;\n padding-left: 20px;\n margin-bottom: 0;\n font-weight: normal;\n cursor: pointer;\n}\n.radio input[type=\"radio\"],\n.radio-inline input[type=\"radio\"],\n.checkbox input[type=\"checkbox\"],\n.checkbox-inline input[type=\"checkbox\"] {\n position: absolute;\n margin-left: -20px;\n margin-top: 4px \\9;\n}\n.radio + .radio,\n.checkbox + .checkbox {\n margin-top: -5px;\n}\n.radio-inline,\n.checkbox-inline {\n position: relative;\n display: inline-block;\n padding-left: 20px;\n margin-bottom: 0;\n vertical-align: middle;\n font-weight: normal;\n cursor: pointer;\n}\n.radio-inline + .radio-inline,\n.checkbox-inline + .checkbox-inline {\n margin-top: 0;\n margin-left: 10px;\n}\ninput[type=\"radio\"][disabled],\ninput[type=\"checkbox\"][disabled],\ninput[type=\"radio\"].disabled,\ninput[type=\"checkbox\"].disabled,\nfieldset[disabled] input[type=\"radio\"],\nfieldset[disabled] input[type=\"checkbox\"] {\n cursor: not-allowed;\n}\n.radio-inline.disabled,\n.checkbox-inline.disabled,\nfieldset[disabled] .radio-inline,\nfieldset[disabled] .checkbox-inline {\n cursor: not-allowed;\n}\n.radio.disabled label,\n.checkbox.disabled label,\nfieldset[disabled] .radio label,\nfieldset[disabled] .checkbox label {\n cursor: not-allowed;\n}\n.form-control-static {\n padding-top: 7px;\n padding-bottom: 7px;\n margin-bottom: 0;\n min-height: 34px;\n}\n.form-control-static.input-lg,\n.form-control-static.input-sm {\n padding-left: 0;\n padding-right: 0;\n}\n.input-sm {\n height: 30px;\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\nselect.input-sm {\n height: 30px;\n line-height: 30px;\n}\ntextarea.input-sm,\nselect[multiple].input-sm {\n height: auto;\n}\n.form-group-sm .form-control {\n height: 30px;\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\n.form-group-sm select.form-control {\n height: 30px;\n line-height: 30px;\n}\n.form-group-sm textarea.form-control,\n.form-group-sm select[multiple].form-control {\n height: auto;\n}\n.form-group-sm .form-control-static {\n height: 30px;\n min-height: 32px;\n padding: 6px 10px;\n font-size: 12px;\n line-height: 1.5;\n}\n.input-lg {\n height: 46px;\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\nselect.input-lg {\n height: 46px;\n line-height: 46px;\n}\ntextarea.input-lg,\nselect[multiple].input-lg {\n height: auto;\n}\n.form-group-lg .form-control {\n height: 46px;\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\n.form-group-lg select.form-control {\n height: 46px;\n line-height: 46px;\n}\n.form-group-lg textarea.form-control,\n.form-group-lg select[multiple].form-control {\n height: auto;\n}\n.form-group-lg .form-control-static {\n height: 46px;\n min-height: 38px;\n padding: 11px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n}\n.has-feedback {\n position: relative;\n}\n.has-feedback .form-control {\n padding-right: 42.5px;\n}\n.form-control-feedback {\n position: absolute;\n top: 0;\n right: 0;\n z-index: 2;\n display: block;\n width: 34px;\n height: 34px;\n line-height: 34px;\n text-align: center;\n pointer-events: none;\n}\n.input-lg + .form-control-feedback,\n.input-group-lg + .form-control-feedback,\n.form-group-lg .form-control + .form-control-feedback {\n width: 46px;\n height: 46px;\n line-height: 46px;\n}\n.input-sm + .form-control-feedback,\n.input-group-sm + .form-control-feedback,\n.form-group-sm .form-control + .form-control-feedback {\n width: 30px;\n height: 30px;\n line-height: 30px;\n}\n.has-success .help-block,\n.has-success .control-label,\n.has-success .radio,\n.has-success .checkbox,\n.has-success .radio-inline,\n.has-success .checkbox-inline,\n.has-success.radio label,\n.has-success.checkbox label,\n.has-success.radio-inline label,\n.has-success.checkbox-inline label {\n color: #3c763d;\n}\n.has-success .form-control {\n border-color: #3c763d;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-success .form-control:focus {\n border-color: #2b542c;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;\n}\n.has-success .input-group-addon {\n color: #3c763d;\n border-color: #3c763d;\n background-color: #dff0d8;\n}\n.has-success .form-control-feedback {\n color: #3c763d;\n}\n.has-warning .help-block,\n.has-warning .control-label,\n.has-warning .radio,\n.has-warning .checkbox,\n.has-warning .radio-inline,\n.has-warning .checkbox-inline,\n.has-warning.radio label,\n.has-warning.checkbox label,\n.has-warning.radio-inline label,\n.has-warning.checkbox-inline label {\n color: #8a6d3b;\n}\n.has-warning .form-control {\n border-color: #8a6d3b;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-warning .form-control:focus {\n border-color: #66512c;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;\n}\n.has-warning .input-group-addon {\n color: #8a6d3b;\n border-color: #8a6d3b;\n background-color: #fcf8e3;\n}\n.has-warning .form-control-feedback {\n color: #8a6d3b;\n}\n.has-error .help-block,\n.has-error .control-label,\n.has-error .radio,\n.has-error .checkbox,\n.has-error .radio-inline,\n.has-error .checkbox-inline,\n.has-error.radio label,\n.has-error.checkbox label,\n.has-error.radio-inline label,\n.has-error.checkbox-inline label {\n color: #a94442;\n}\n.has-error .form-control {\n border-color: #a94442;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-error .form-control:focus {\n border-color: #843534;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;\n}\n.has-error .input-group-addon {\n color: #a94442;\n border-color: #a94442;\n background-color: #f2dede;\n}\n.has-error .form-control-feedback {\n color: #a94442;\n}\n.has-feedback label ~ .form-control-feedback {\n top: 25px;\n}\n.has-feedback label.sr-only ~ .form-control-feedback {\n top: 0;\n}\n.help-block {\n display: block;\n margin-top: 5px;\n margin-bottom: 10px;\n color: #737373;\n}\n@media (min-width: 768px) {\n .form-inline .form-group {\n display: inline-block;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .form-inline .form-control {\n display: inline-block;\n width: auto;\n vertical-align: middle;\n }\n .form-inline .form-control-static {\n display: inline-block;\n }\n .form-inline .input-group {\n display: inline-table;\n vertical-align: middle;\n }\n .form-inline .input-group .input-group-addon,\n .form-inline .input-group .input-group-btn,\n .form-inline .input-group .form-control {\n width: auto;\n }\n .form-inline .input-group > .form-control {\n width: 100%;\n }\n .form-inline .control-label {\n margin-bottom: 0;\n vertical-align: middle;\n }\n .form-inline .radio,\n .form-inline .checkbox {\n display: inline-block;\n margin-top: 0;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .form-inline .radio label,\n .form-inline .checkbox label {\n padding-left: 0;\n }\n .form-inline .radio input[type=\"radio\"],\n .form-inline .checkbox input[type=\"checkbox\"] {\n position: relative;\n margin-left: 0;\n }\n .form-inline .has-feedback .form-control-feedback {\n top: 0;\n }\n}\n.form-horizontal .radio,\n.form-horizontal .checkbox,\n.form-horizontal .radio-inline,\n.form-horizontal .checkbox-inline {\n margin-top: 0;\n margin-bottom: 0;\n padding-top: 7px;\n}\n.form-horizontal .radio,\n.form-horizontal .checkbox {\n min-height: 27px;\n}\n.form-horizontal .form-group {\n margin-left: -15px;\n margin-right: -15px;\n}\n@media (min-width: 768px) {\n .form-horizontal .control-label {\n text-align: right;\n margin-bottom: 0;\n padding-top: 7px;\n }\n}\n.form-horizontal .has-feedback .form-control-feedback {\n right: 15px;\n}\n@media (min-width: 768px) {\n .form-horizontal .form-group-lg .control-label {\n padding-top: 11px;\n font-size: 18px;\n }\n}\n@media (min-width: 768px) {\n .form-horizontal .form-group-sm .control-label {\n padding-top: 6px;\n font-size: 12px;\n }\n}\n.btn {\n display: inline-block;\n margin-bottom: 0;\n font-weight: normal;\n text-align: center;\n vertical-align: middle;\n touch-action: manipulation;\n cursor: pointer;\n background-image: none;\n border: 1px solid transparent;\n white-space: nowrap;\n padding: 6px 12px;\n font-size: 14px;\n line-height: 1.42857143;\n border-radius: 4px;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n}\n.btn:focus,\n.btn:active:focus,\n.btn.active:focus,\n.btn.focus,\n.btn:active.focus,\n.btn.active.focus {\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\n.btn:hover,\n.btn:focus,\n.btn.focus {\n color: #333;\n text-decoration: none;\n}\n.btn:active,\n.btn.active {\n outline: 0;\n background-image: none;\n -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn.disabled,\n.btn[disabled],\nfieldset[disabled] .btn {\n cursor: not-allowed;\n opacity: 0.65;\n filter: alpha(opacity=65);\n -webkit-box-shadow: none;\n box-shadow: none;\n}\na.btn.disabled,\nfieldset[disabled] a.btn {\n pointer-events: none;\n}\n.btn-default {\n color: #333;\n background-color: #fff;\n border-color: #ccc;\n}\n.btn-default:focus,\n.btn-default.focus {\n color: #333;\n background-color: #e6e6e6;\n border-color: #8c8c8c;\n}\n.btn-default:hover {\n color: #333;\n background-color: #e6e6e6;\n border-color: #adadad;\n}\n.btn-default:active,\n.btn-default.active,\n.open > .dropdown-toggle.btn-default {\n color: #333;\n background-color: #e6e6e6;\n border-color: #adadad;\n}\n.btn-default:active:hover,\n.btn-default.active:hover,\n.open > .dropdown-toggle.btn-default:hover,\n.btn-default:active:focus,\n.btn-default.active:focus,\n.open > .dropdown-toggle.btn-default:focus,\n.btn-default:active.focus,\n.btn-default.active.focus,\n.open > .dropdown-toggle.btn-default.focus {\n color: #333;\n background-color: #d4d4d4;\n border-color: #8c8c8c;\n}\n.btn-default:active,\n.btn-default.active,\n.open > .dropdown-toggle.btn-default {\n background-image: none;\n}\n.btn-default.disabled:hover,\n.btn-default[disabled]:hover,\nfieldset[disabled] .btn-default:hover,\n.btn-default.disabled:focus,\n.btn-default[disabled]:focus,\nfieldset[disabled] .btn-default:focus,\n.btn-default.disabled.focus,\n.btn-default[disabled].focus,\nfieldset[disabled] .btn-default.focus {\n background-color: #fff;\n border-color: #ccc;\n}\n.btn-default .badge {\n color: #fff;\n background-color: #333;\n}\n.btn-primary {\n color: #fff;\n background-color: #337ab7;\n border-color: #2e6da4;\n}\n.btn-primary:focus,\n.btn-primary.focus {\n color: #fff;\n background-color: #286090;\n border-color: #122b40;\n}\n.btn-primary:hover {\n color: #fff;\n background-color: #286090;\n border-color: #204d74;\n}\n.btn-primary:active,\n.btn-primary.active,\n.open > .dropdown-toggle.btn-primary {\n color: #fff;\n background-color: #286090;\n border-color: #204d74;\n}\n.btn-primary:active:hover,\n.btn-primary.active:hover,\n.open > .dropdown-toggle.btn-primary:hover,\n.btn-primary:active:focus,\n.btn-primary.active:focus,\n.open > .dropdown-toggle.btn-primary:focus,\n.btn-primary:active.focus,\n.btn-primary.active.focus,\n.open > .dropdown-toggle.btn-primary.focus {\n color: #fff;\n background-color: #204d74;\n border-color: #122b40;\n}\n.btn-primary:active,\n.btn-primary.active,\n.open > .dropdown-toggle.btn-primary {\n background-image: none;\n}\n.btn-primary.disabled:hover,\n.btn-primary[disabled]:hover,\nfieldset[disabled] .btn-primary:hover,\n.btn-primary.disabled:focus,\n.btn-primary[disabled]:focus,\nfieldset[disabled] .btn-primary:focus,\n.btn-primary.disabled.focus,\n.btn-primary[disabled].focus,\nfieldset[disabled] .btn-primary.focus {\n background-color: #337ab7;\n border-color: #2e6da4;\n}\n.btn-primary .badge {\n color: #337ab7;\n background-color: #fff;\n}\n.btn-success {\n color: #fff;\n background-color: #5cb85c;\n border-color: #4cae4c;\n}\n.btn-success:focus,\n.btn-success.focus {\n color: #fff;\n background-color: #449d44;\n border-color: #255625;\n}\n.btn-success:hover {\n color: #fff;\n background-color: #449d44;\n border-color: #398439;\n}\n.btn-success:active,\n.btn-success.active,\n.open > .dropdown-toggle.btn-success {\n color: #fff;\n background-color: #449d44;\n border-color: #398439;\n}\n.btn-success:active:hover,\n.btn-success.active:hover,\n.open > .dropdown-toggle.btn-success:hover,\n.btn-success:active:focus,\n.btn-success.active:focus,\n.open > .dropdown-toggle.btn-success:focus,\n.btn-success:active.focus,\n.btn-success.active.focus,\n.open > .dropdown-toggle.btn-success.focus {\n color: #fff;\n background-color: #398439;\n border-color: #255625;\n}\n.btn-success:active,\n.btn-success.active,\n.open > .dropdown-toggle.btn-success {\n background-image: none;\n}\n.btn-success.disabled:hover,\n.btn-success[disabled]:hover,\nfieldset[disabled] .btn-success:hover,\n.btn-success.disabled:focus,\n.btn-success[disabled]:focus,\nfieldset[disabled] .btn-success:focus,\n.btn-success.disabled.focus,\n.btn-success[disabled].focus,\nfieldset[disabled] .btn-success.focus {\n background-color: #5cb85c;\n border-color: #4cae4c;\n}\n.btn-success .badge {\n color: #5cb85c;\n background-color: #fff;\n}\n.btn-info {\n color: #fff;\n background-color: #5bc0de;\n border-color: #46b8da;\n}\n.btn-info:focus,\n.btn-info.focus {\n color: #fff;\n background-color: #31b0d5;\n border-color: #1b6d85;\n}\n.btn-info:hover {\n color: #fff;\n background-color: #31b0d5;\n border-color: #269abc;\n}\n.btn-info:active,\n.btn-info.active,\n.open > .dropdown-toggle.btn-info {\n color: #fff;\n background-color: #31b0d5;\n border-color: #269abc;\n}\n.btn-info:active:hover,\n.btn-info.active:hover,\n.open > .dropdown-toggle.btn-info:hover,\n.btn-info:active:focus,\n.btn-info.active:focus,\n.open > .dropdown-toggle.btn-info:focus,\n.btn-info:active.focus,\n.btn-info.active.focus,\n.open > .dropdown-toggle.btn-info.focus {\n color: #fff;\n background-color: #269abc;\n border-color: #1b6d85;\n}\n.btn-info:active,\n.btn-info.active,\n.open > .dropdown-toggle.btn-info {\n background-image: none;\n}\n.btn-info.disabled:hover,\n.btn-info[disabled]:hover,\nfieldset[disabled] .btn-info:hover,\n.btn-info.disabled:focus,\n.btn-info[disabled]:focus,\nfieldset[disabled] .btn-info:focus,\n.btn-info.disabled.focus,\n.btn-info[disabled].focus,\nfieldset[disabled] .btn-info.focus {\n background-color: #5bc0de;\n border-color: #46b8da;\n}\n.btn-info .badge {\n color: #5bc0de;\n background-color: #fff;\n}\n.btn-warning {\n color: #fff;\n background-color: #f0ad4e;\n border-color: #eea236;\n}\n.btn-warning:focus,\n.btn-warning.focus {\n color: #fff;\n background-color: #ec971f;\n border-color: #985f0d;\n}\n.btn-warning:hover {\n color: #fff;\n background-color: #ec971f;\n border-color: #d58512;\n}\n.btn-warning:active,\n.btn-warning.active,\n.open > .dropdown-toggle.btn-warning {\n color: #fff;\n background-color: #ec971f;\n border-color: #d58512;\n}\n.btn-warning:active:hover,\n.btn-warning.active:hover,\n.open > .dropdown-toggle.btn-warning:hover,\n.btn-warning:active:focus,\n.btn-warning.active:focus,\n.open > .dropdown-toggle.btn-warning:focus,\n.btn-warning:active.focus,\n.btn-warning.active.focus,\n.open > .dropdown-toggle.btn-warning.focus {\n color: #fff;\n background-color: #d58512;\n border-color: #985f0d;\n}\n.btn-warning:active,\n.btn-warning.active,\n.open > .dropdown-toggle.btn-warning {\n background-image: none;\n}\n.btn-warning.disabled:hover,\n.btn-warning[disabled]:hover,\nfieldset[disabled] .btn-warning:hover,\n.btn-warning.disabled:focus,\n.btn-warning[disabled]:focus,\nfieldset[disabled] .btn-warning:focus,\n.btn-warning.disabled.focus,\n.btn-warning[disabled].focus,\nfieldset[disabled] .btn-warning.focus {\n background-color: #f0ad4e;\n border-color: #eea236;\n}\n.btn-warning .badge {\n color: #f0ad4e;\n background-color: #fff;\n}\n.btn-danger {\n color: #fff;\n background-color: #d9534f;\n border-color: #d43f3a;\n}\n.btn-danger:focus,\n.btn-danger.focus {\n color: #fff;\n background-color: #c9302c;\n border-color: #761c19;\n}\n.btn-danger:hover {\n color: #fff;\n background-color: #c9302c;\n border-color: #ac2925;\n}\n.btn-danger:active,\n.btn-danger.active,\n.open > .dropdown-toggle.btn-danger {\n color: #fff;\n background-color: #c9302c;\n border-color: #ac2925;\n}\n.btn-danger:active:hover,\n.btn-danger.active:hover,\n.open > .dropdown-toggle.btn-danger:hover,\n.btn-danger:active:focus,\n.btn-danger.active:focus,\n.open > .dropdown-toggle.btn-danger:focus,\n.btn-danger:active.focus,\n.btn-danger.active.focus,\n.open > .dropdown-toggle.btn-danger.focus {\n color: #fff;\n background-color: #ac2925;\n border-color: #761c19;\n}\n.btn-danger:active,\n.btn-danger.active,\n.open > .dropdown-toggle.btn-danger {\n background-image: none;\n}\n.btn-danger.disabled:hover,\n.btn-danger[disabled]:hover,\nfieldset[disabled] .btn-danger:hover,\n.btn-danger.disabled:focus,\n.btn-danger[disabled]:focus,\nfieldset[disabled] .btn-danger:focus,\n.btn-danger.disabled.focus,\n.btn-danger[disabled].focus,\nfieldset[disabled] .btn-danger.focus {\n background-color: #d9534f;\n border-color: #d43f3a;\n}\n.btn-danger .badge {\n color: #d9534f;\n background-color: #fff;\n}\n.btn-link {\n color: #337ab7;\n font-weight: normal;\n border-radius: 0;\n}\n.btn-link,\n.btn-link:active,\n.btn-link.active,\n.btn-link[disabled],\nfieldset[disabled] .btn-link {\n background-color: transparent;\n -webkit-box-shadow: none;\n box-shadow: none;\n}\n.btn-link,\n.btn-link:hover,\n.btn-link:focus,\n.btn-link:active {\n border-color: transparent;\n}\n.btn-link:hover,\n.btn-link:focus {\n color: #23527c;\n text-decoration: underline;\n background-color: transparent;\n}\n.btn-link[disabled]:hover,\nfieldset[disabled] .btn-link:hover,\n.btn-link[disabled]:focus,\nfieldset[disabled] .btn-link:focus {\n color: #777777;\n text-decoration: none;\n}\n.btn-lg,\n.btn-group-lg > .btn {\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\n.btn-sm,\n.btn-group-sm > .btn {\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\n.btn-xs,\n.btn-group-xs > .btn {\n padding: 1px 5px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\n.btn-block {\n display: block;\n width: 100%;\n}\n.btn-block + .btn-block {\n margin-top: 5px;\n}\ninput[type=\"submit\"].btn-block,\ninput[type=\"reset\"].btn-block,\ninput[type=\"button\"].btn-block {\n width: 100%;\n}\n.fade {\n opacity: 0;\n -webkit-transition: opacity 0.15s linear;\n -o-transition: opacity 0.15s linear;\n transition: opacity 0.15s linear;\n}\n.fade.in {\n opacity: 1;\n}\n.collapse {\n display: none;\n}\n.collapse.in {\n display: block;\n}\ntr.collapse.in {\n display: table-row;\n}\ntbody.collapse.in {\n display: table-row-group;\n}\n.collapsing {\n position: relative;\n height: 0;\n overflow: hidden;\n -webkit-transition-property: height, visibility;\n transition-property: height, visibility;\n -webkit-transition-duration: 0.35s;\n transition-duration: 0.35s;\n -webkit-transition-timing-function: ease;\n transition-timing-function: ease;\n}\n.caret {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 2px;\n vertical-align: middle;\n border-top: 4px dashed;\n border-top: 4px solid \\9;\n border-right: 4px solid transparent;\n border-left: 4px solid transparent;\n}\n.dropup,\n.dropdown {\n position: relative;\n}\n.dropdown-toggle:focus {\n outline: 0;\n}\n.dropdown-menu {\n position: absolute;\n top: 100%;\n left: 0;\n z-index: 1000;\n display: none;\n float: left;\n min-width: 160px;\n padding: 5px 0;\n margin: 2px 0 0;\n list-style: none;\n font-size: 14px;\n text-align: left;\n background-color: #fff;\n border: 1px solid #ccc;\n border: 1px solid rgba(0, 0, 0, 0.15);\n border-radius: 4px;\n -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\n box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\n background-clip: padding-box;\n}\n.dropdown-menu.pull-right {\n right: 0;\n left: auto;\n}\n.dropdown-menu .divider {\n height: 1px;\n margin: 9px 0;\n overflow: hidden;\n background-color: #e5e5e5;\n}\n.dropdown-menu > li > a {\n display: block;\n padding: 3px 20px;\n clear: both;\n font-weight: normal;\n line-height: 1.42857143;\n color: #333333;\n white-space: nowrap;\n}\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n text-decoration: none;\n color: #262626;\n background-color: #f5f5f5;\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n color: #fff;\n text-decoration: none;\n outline: 0;\n background-color: #337ab7;\n}\n.dropdown-menu > .disabled > a,\n.dropdown-menu > .disabled > a:hover,\n.dropdown-menu > .disabled > a:focus {\n color: #777777;\n}\n.dropdown-menu > .disabled > a:hover,\n.dropdown-menu > .disabled > a:focus {\n text-decoration: none;\n background-color: transparent;\n background-image: none;\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n cursor: not-allowed;\n}\n.open > .dropdown-menu {\n display: block;\n}\n.open > a {\n outline: 0;\n}\n.dropdown-menu-right {\n left: auto;\n right: 0;\n}\n.dropdown-menu-left {\n left: 0;\n right: auto;\n}\n.dropdown-header {\n display: block;\n padding: 3px 20px;\n font-size: 12px;\n line-height: 1.42857143;\n color: #777777;\n white-space: nowrap;\n}\n.dropdown-backdrop {\n position: fixed;\n left: 0;\n right: 0;\n bottom: 0;\n top: 0;\n z-index: 990;\n}\n.pull-right > .dropdown-menu {\n right: 0;\n left: auto;\n}\n.dropup .caret,\n.navbar-fixed-bottom .dropdown .caret {\n border-top: 0;\n border-bottom: 4px dashed;\n border-bottom: 4px solid \\9;\n content: \"\";\n}\n.dropup .dropdown-menu,\n.navbar-fixed-bottom .dropdown .dropdown-menu {\n top: auto;\n bottom: 100%;\n margin-bottom: 2px;\n}\n@media (min-width: 768px) {\n .navbar-right .dropdown-menu {\n left: auto;\n right: 0;\n }\n .navbar-right .dropdown-menu-left {\n left: 0;\n right: auto;\n }\n}\n.btn-group,\n.btn-group-vertical {\n position: relative;\n display: inline-block;\n vertical-align: middle;\n}\n.btn-group > .btn,\n.btn-group-vertical > .btn {\n position: relative;\n float: left;\n}\n.btn-group > .btn:hover,\n.btn-group-vertical > .btn:hover,\n.btn-group > .btn:focus,\n.btn-group-vertical > .btn:focus,\n.btn-group > .btn:active,\n.btn-group-vertical > .btn:active,\n.btn-group > .btn.active,\n.btn-group-vertical > .btn.active {\n z-index: 2;\n}\n.btn-group .btn + .btn,\n.btn-group .btn + .btn-group,\n.btn-group .btn-group + .btn,\n.btn-group .btn-group + .btn-group {\n margin-left: -1px;\n}\n.btn-toolbar {\n margin-left: -5px;\n}\n.btn-toolbar .btn,\n.btn-toolbar .btn-group,\n.btn-toolbar .input-group {\n float: left;\n}\n.btn-toolbar > .btn,\n.btn-toolbar > .btn-group,\n.btn-toolbar > .input-group {\n margin-left: 5px;\n}\n.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {\n border-radius: 0;\n}\n.btn-group > .btn:first-child {\n margin-left: 0;\n}\n.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) {\n border-bottom-right-radius: 0;\n border-top-right-radius: 0;\n}\n.btn-group > .btn:last-child:not(:first-child),\n.btn-group > .dropdown-toggle:not(:first-child) {\n border-bottom-left-radius: 0;\n border-top-left-radius: 0;\n}\n.btn-group > .btn-group {\n float: left;\n}\n.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {\n border-radius: 0;\n}\n.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child,\n.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle {\n border-bottom-right-radius: 0;\n border-top-right-radius: 0;\n}\n.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child {\n border-bottom-left-radius: 0;\n border-top-left-radius: 0;\n}\n.btn-group .dropdown-toggle:active,\n.btn-group.open .dropdown-toggle {\n outline: 0;\n}\n.btn-group > .btn + .dropdown-toggle {\n padding-left: 8px;\n padding-right: 8px;\n}\n.btn-group > .btn-lg + .dropdown-toggle {\n padding-left: 12px;\n padding-right: 12px;\n}\n.btn-group.open .dropdown-toggle {\n -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn-group.open .dropdown-toggle.btn-link {\n -webkit-box-shadow: none;\n box-shadow: none;\n}\n.btn .caret {\n margin-left: 0;\n}\n.btn-lg .caret {\n border-width: 5px 5px 0;\n border-bottom-width: 0;\n}\n.dropup .btn-lg .caret {\n border-width: 0 5px 5px;\n}\n.btn-group-vertical > .btn,\n.btn-group-vertical > .btn-group,\n.btn-group-vertical > .btn-group > .btn {\n display: block;\n float: none;\n width: 100%;\n max-width: 100%;\n}\n.btn-group-vertical > .btn-group > .btn {\n float: none;\n}\n.btn-group-vertical > .btn + .btn,\n.btn-group-vertical > .btn + .btn-group,\n.btn-group-vertical > .btn-group + .btn,\n.btn-group-vertical > .btn-group + .btn-group {\n margin-top: -1px;\n margin-left: 0;\n}\n.btn-group-vertical > .btn:not(:first-child):not(:last-child) {\n border-radius: 0;\n}\n.btn-group-vertical > .btn:first-child:not(:last-child) {\n border-top-right-radius: 4px;\n border-top-left-radius: 4px;\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n.btn-group-vertical > .btn:last-child:not(:first-child) {\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n border-bottom-right-radius: 4px;\n border-bottom-left-radius: 4px;\n}\n.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {\n border-radius: 0;\n}\n.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child,\n.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle {\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.btn-group-justified {\n display: table;\n width: 100%;\n table-layout: fixed;\n border-collapse: separate;\n}\n.btn-group-justified > .btn,\n.btn-group-justified > .btn-group {\n float: none;\n display: table-cell;\n width: 1%;\n}\n.btn-group-justified > .btn-group .btn {\n width: 100%;\n}\n.btn-group-justified > .btn-group .dropdown-menu {\n left: auto;\n}\n[data-toggle=\"buttons\"] > .btn input[type=\"radio\"],\n[data-toggle=\"buttons\"] > .btn-group > .btn input[type=\"radio\"],\n[data-toggle=\"buttons\"] > .btn input[type=\"checkbox\"],\n[data-toggle=\"buttons\"] > .btn-group > .btn input[type=\"checkbox\"] {\n position: absolute;\n clip: rect(0, 0, 0, 0);\n pointer-events: none;\n}\n.input-group {\n position: relative;\n display: table;\n border-collapse: separate;\n}\n.input-group[class*=\"col-\"] {\n float: none;\n padding-left: 0;\n padding-right: 0;\n}\n.input-group .form-control {\n position: relative;\n z-index: 2;\n float: left;\n width: 100%;\n margin-bottom: 0;\n}\n.input-group .form-control:focus {\n z-index: 3;\n}\n.input-group-lg > .form-control,\n.input-group-lg > .input-group-addon,\n.input-group-lg > .input-group-btn > .btn {\n height: 46px;\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\nselect.input-group-lg > .form-control,\nselect.input-group-lg > .input-group-addon,\nselect.input-group-lg > .input-group-btn > .btn {\n height: 46px;\n line-height: 46px;\n}\ntextarea.input-group-lg > .form-control,\ntextarea.input-group-lg > .input-group-addon,\ntextarea.input-group-lg > .input-group-btn > .btn,\nselect[multiple].input-group-lg > .form-control,\nselect[multiple].input-group-lg > .input-group-addon,\nselect[multiple].input-group-lg > .input-group-btn > .btn {\n height: auto;\n}\n.input-group-sm > .form-control,\n.input-group-sm > .input-group-addon,\n.input-group-sm > .input-group-btn > .btn {\n height: 30px;\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\nselect.input-group-sm > .form-control,\nselect.input-group-sm > .input-group-addon,\nselect.input-group-sm > .input-group-btn > .btn {\n height: 30px;\n line-height: 30px;\n}\ntextarea.input-group-sm > .form-control,\ntextarea.input-group-sm > .input-group-addon,\ntextarea.input-group-sm > .input-group-btn > .btn,\nselect[multiple].input-group-sm > .form-control,\nselect[multiple].input-group-sm > .input-group-addon,\nselect[multiple].input-group-sm > .input-group-btn > .btn {\n height: auto;\n}\n.input-group-addon,\n.input-group-btn,\n.input-group .form-control {\n display: table-cell;\n}\n.input-group-addon:not(:first-child):not(:last-child),\n.input-group-btn:not(:first-child):not(:last-child),\n.input-group .form-control:not(:first-child):not(:last-child) {\n border-radius: 0;\n}\n.input-group-addon,\n.input-group-btn {\n width: 1%;\n white-space: nowrap;\n vertical-align: middle;\n}\n.input-group-addon {\n padding: 6px 12px;\n font-size: 14px;\n font-weight: normal;\n line-height: 1;\n color: #555555;\n text-align: center;\n background-color: #eeeeee;\n border: 1px solid #ccc;\n border-radius: 4px;\n}\n.input-group-addon.input-sm {\n padding: 5px 10px;\n font-size: 12px;\n border-radius: 3px;\n}\n.input-group-addon.input-lg {\n padding: 10px 16px;\n font-size: 18px;\n border-radius: 6px;\n}\n.input-group-addon input[type=\"radio\"],\n.input-group-addon input[type=\"checkbox\"] {\n margin-top: 0;\n}\n.input-group .form-control:first-child,\n.input-group-addon:first-child,\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group > .btn,\n.input-group-btn:first-child > .dropdown-toggle,\n.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {\n border-bottom-right-radius: 0;\n border-top-right-radius: 0;\n}\n.input-group-addon:first-child {\n border-right: 0;\n}\n.input-group .form-control:last-child,\n.input-group-addon:last-child,\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group > .btn,\n.input-group-btn:last-child > .dropdown-toggle,\n.input-group-btn:first-child > .btn:not(:first-child),\n.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {\n border-bottom-left-radius: 0;\n border-top-left-radius: 0;\n}\n.input-group-addon:last-child {\n border-left: 0;\n}\n.input-group-btn {\n position: relative;\n font-size: 0;\n white-space: nowrap;\n}\n.input-group-btn > .btn {\n position: relative;\n}\n.input-group-btn > .btn + .btn {\n margin-left: -1px;\n}\n.input-group-btn > .btn:hover,\n.input-group-btn > .btn:focus,\n.input-group-btn > .btn:active {\n z-index: 2;\n}\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group {\n margin-right: -1px;\n}\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group {\n z-index: 2;\n margin-left: -1px;\n}\n.nav {\n margin-bottom: 0;\n padding-left: 0;\n list-style: none;\n}\n.nav > li {\n position: relative;\n display: block;\n}\n.nav > li > a {\n position: relative;\n display: block;\n padding: 10px 15px;\n}\n.nav > li > a:hover,\n.nav > li > a:focus {\n text-decoration: none;\n background-color: #eeeeee;\n}\n.nav > li.disabled > a {\n color: #777777;\n}\n.nav > li.disabled > a:hover,\n.nav > li.disabled > a:focus {\n color: #777777;\n text-decoration: none;\n background-color: transparent;\n cursor: not-allowed;\n}\n.nav .open > a,\n.nav .open > a:hover,\n.nav .open > a:focus {\n background-color: #eeeeee;\n border-color: #337ab7;\n}\n.nav .nav-divider {\n height: 1px;\n margin: 9px 0;\n overflow: hidden;\n background-color: #e5e5e5;\n}\n.nav > li > a > img {\n max-width: none;\n}\n.nav-tabs {\n border-bottom: 1px solid #ddd;\n}\n.nav-tabs > li {\n float: left;\n margin-bottom: -1px;\n}\n.nav-tabs > li > a {\n margin-right: 2px;\n line-height: 1.42857143;\n border: 1px solid transparent;\n border-radius: 4px 4px 0 0;\n}\n.nav-tabs > li > a:hover {\n border-color: #eeeeee #eeeeee #ddd;\n}\n.nav-tabs > li.active > a,\n.nav-tabs > li.active > a:hover,\n.nav-tabs > li.active > a:focus {\n color: #555555;\n background-color: #fff;\n border: 1px solid #ddd;\n border-bottom-color: transparent;\n cursor: default;\n}\n.nav-tabs.nav-justified {\n width: 100%;\n border-bottom: 0;\n}\n.nav-tabs.nav-justified > li {\n float: none;\n}\n.nav-tabs.nav-justified > li > a {\n text-align: center;\n margin-bottom: 5px;\n}\n.nav-tabs.nav-justified > .dropdown .dropdown-menu {\n top: auto;\n left: auto;\n}\n@media (min-width: 768px) {\n .nav-tabs.nav-justified > li {\n display: table-cell;\n width: 1%;\n }\n .nav-tabs.nav-justified > li > a {\n margin-bottom: 0;\n }\n}\n.nav-tabs.nav-justified > li > a {\n margin-right: 0;\n border-radius: 4px;\n}\n.nav-tabs.nav-justified > .active > a,\n.nav-tabs.nav-justified > .active > a:hover,\n.nav-tabs.nav-justified > .active > a:focus {\n border: 1px solid #ddd;\n}\n@media (min-width: 768px) {\n .nav-tabs.nav-justified > li > a {\n border-bottom: 1px solid #ddd;\n border-radius: 4px 4px 0 0;\n }\n .nav-tabs.nav-justified > .active > a,\n .nav-tabs.nav-justified > .active > a:hover,\n .nav-tabs.nav-justified > .active > a:focus {\n border-bottom-color: #fff;\n }\n}\n.nav-pills > li {\n float: left;\n}\n.nav-pills > li > a {\n border-radius: 4px;\n}\n.nav-pills > li + li {\n margin-left: 2px;\n}\n.nav-pills > li.active > a,\n.nav-pills > li.active > a:hover,\n.nav-pills > li.active > a:focus {\n color: #fff;\n background-color: #337ab7;\n}\n.nav-stacked > li {\n float: none;\n}\n.nav-stacked > li + li {\n margin-top: 2px;\n margin-left: 0;\n}\n.nav-justified {\n width: 100%;\n}\n.nav-justified > li {\n float: none;\n}\n.nav-justified > li > a {\n text-align: center;\n margin-bottom: 5px;\n}\n.nav-justified > .dropdown .dropdown-menu {\n top: auto;\n left: auto;\n}\n@media (min-width: 768px) {\n .nav-justified > li {\n display: table-cell;\n width: 1%;\n }\n .nav-justified > li > a {\n margin-bottom: 0;\n }\n}\n.nav-tabs-justified {\n border-bottom: 0;\n}\n.nav-tabs-justified > li > a {\n margin-right: 0;\n border-radius: 4px;\n}\n.nav-tabs-justified > .active > a,\n.nav-tabs-justified > .active > a:hover,\n.nav-tabs-justified > .active > a:focus {\n border: 1px solid #ddd;\n}\n@media (min-width: 768px) {\n .nav-tabs-justified > li > a {\n border-bottom: 1px solid #ddd;\n border-radius: 4px 4px 0 0;\n }\n .nav-tabs-justified > .active > a,\n .nav-tabs-justified > .active > a:hover,\n .nav-tabs-justified > .active > a:focus {\n border-bottom-color: #fff;\n }\n}\n.tab-content > .tab-pane {\n display: none;\n}\n.tab-content > .active {\n display: block;\n}\n.nav-tabs .dropdown-menu {\n margin-top: -1px;\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.navbar {\n position: relative;\n min-height: 50px;\n margin-bottom: 20px;\n border: 1px solid transparent;\n}\n@media (min-width: 768px) {\n .navbar {\n border-radius: 4px;\n }\n}\n@media (min-width: 768px) {\n .navbar-header {\n float: left;\n }\n}\n.navbar-collapse {\n overflow-x: visible;\n padding-right: 15px;\n padding-left: 15px;\n border-top: 1px solid transparent;\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);\n -webkit-overflow-scrolling: touch;\n}\n.navbar-collapse.in {\n overflow-y: auto;\n}\n@media (min-width: 768px) {\n .navbar-collapse {\n width: auto;\n border-top: 0;\n box-shadow: none;\n }\n .navbar-collapse.collapse {\n display: block !important;\n height: auto !important;\n padding-bottom: 0;\n overflow: visible !important;\n }\n .navbar-collapse.in {\n overflow-y: visible;\n }\n .navbar-fixed-top .navbar-collapse,\n .navbar-static-top .navbar-collapse,\n .navbar-fixed-bottom .navbar-collapse {\n padding-left: 0;\n padding-right: 0;\n }\n}\n.navbar-fixed-top .navbar-collapse,\n.navbar-fixed-bottom .navbar-collapse {\n max-height: 340px;\n}\n@media (max-device-width: 480px) and (orientation: landscape) {\n .navbar-fixed-top .navbar-collapse,\n .navbar-fixed-bottom .navbar-collapse {\n max-height: 200px;\n }\n}\n.container > .navbar-header,\n.container-fluid > .navbar-header,\n.container > .navbar-collapse,\n.container-fluid > .navbar-collapse {\n margin-right: -15px;\n margin-left: -15px;\n}\n@media (min-width: 768px) {\n .container > .navbar-header,\n .container-fluid > .navbar-header,\n .container > .navbar-collapse,\n .container-fluid > .navbar-collapse {\n margin-right: 0;\n margin-left: 0;\n }\n}\n.navbar-static-top {\n z-index: 1000;\n border-width: 0 0 1px;\n}\n@media (min-width: 768px) {\n .navbar-static-top {\n border-radius: 0;\n }\n}\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n position: fixed;\n right: 0;\n left: 0;\n z-index: 1030;\n}\n@media (min-width: 768px) {\n .navbar-fixed-top,\n .navbar-fixed-bottom {\n border-radius: 0;\n }\n}\n.navbar-fixed-top {\n top: 0;\n border-width: 0 0 1px;\n}\n.navbar-fixed-bottom {\n bottom: 0;\n margin-bottom: 0;\n border-width: 1px 0 0;\n}\n.navbar-brand {\n float: left;\n padding: 15px 15px;\n font-size: 18px;\n line-height: 20px;\n height: 50px;\n}\n.navbar-brand:hover,\n.navbar-brand:focus {\n text-decoration: none;\n}\n.navbar-brand > img {\n display: block;\n}\n@media (min-width: 768px) {\n .navbar > .container .navbar-brand,\n .navbar > .container-fluid .navbar-brand {\n margin-left: -15px;\n }\n}\n.navbar-toggle {\n position: relative;\n float: right;\n margin-right: 15px;\n padding: 9px 10px;\n margin-top: 8px;\n margin-bottom: 8px;\n background-color: transparent;\n background-image: none;\n border: 1px solid transparent;\n border-radius: 4px;\n}\n.navbar-toggle:focus {\n outline: 0;\n}\n.navbar-toggle .icon-bar {\n display: block;\n width: 22px;\n height: 2px;\n border-radius: 1px;\n}\n.navbar-toggle .icon-bar + .icon-bar {\n margin-top: 4px;\n}\n@media (min-width: 768px) {\n .navbar-toggle {\n display: none;\n }\n}\n.navbar-nav {\n margin: 7.5px -15px;\n}\n.navbar-nav > li > a {\n padding-top: 10px;\n padding-bottom: 10px;\n line-height: 20px;\n}\n@media (max-width: 767px) {\n .navbar-nav .open .dropdown-menu {\n position: static;\n float: none;\n width: auto;\n margin-top: 0;\n background-color: transparent;\n border: 0;\n box-shadow: none;\n }\n .navbar-nav .open .dropdown-menu > li > a,\n .navbar-nav .open .dropdown-menu .dropdown-header {\n padding: 5px 15px 5px 25px;\n }\n .navbar-nav .open .dropdown-menu > li > a {\n line-height: 20px;\n }\n .navbar-nav .open .dropdown-menu > li > a:hover,\n .navbar-nav .open .dropdown-menu > li > a:focus {\n background-image: none;\n }\n}\n@media (min-width: 768px) {\n .navbar-nav {\n float: left;\n margin: 0;\n }\n .navbar-nav > li {\n float: left;\n }\n .navbar-nav > li > a {\n padding-top: 15px;\n padding-bottom: 15px;\n }\n}\n.navbar-form {\n margin-left: -15px;\n margin-right: -15px;\n padding: 10px 15px;\n border-top: 1px solid transparent;\n border-bottom: 1px solid transparent;\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);\n margin-top: 8px;\n margin-bottom: 8px;\n}\n@media (min-width: 768px) {\n .navbar-form .form-group {\n display: inline-block;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .navbar-form .form-control {\n display: inline-block;\n width: auto;\n vertical-align: middle;\n }\n .navbar-form .form-control-static {\n display: inline-block;\n }\n .navbar-form .input-group {\n display: inline-table;\n vertical-align: middle;\n }\n .navbar-form .input-group .input-group-addon,\n .navbar-form .input-group .input-group-btn,\n .navbar-form .input-group .form-control {\n width: auto;\n }\n .navbar-form .input-group > .form-control {\n width: 100%;\n }\n .navbar-form .control-label {\n margin-bottom: 0;\n vertical-align: middle;\n }\n .navbar-form .radio,\n .navbar-form .checkbox {\n display: inline-block;\n margin-top: 0;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .navbar-form .radio label,\n .navbar-form .checkbox label {\n padding-left: 0;\n }\n .navbar-form .radio input[type=\"radio\"],\n .navbar-form .checkbox input[type=\"checkbox\"] {\n position: relative;\n margin-left: 0;\n }\n .navbar-form .has-feedback .form-control-feedback {\n top: 0;\n }\n}\n@media (max-width: 767px) {\n .navbar-form .form-group {\n margin-bottom: 5px;\n }\n .navbar-form .form-group:last-child {\n margin-bottom: 0;\n }\n}\n@media (min-width: 768px) {\n .navbar-form {\n width: auto;\n border: 0;\n margin-left: 0;\n margin-right: 0;\n padding-top: 0;\n padding-bottom: 0;\n -webkit-box-shadow: none;\n box-shadow: none;\n }\n}\n.navbar-nav > li > .dropdown-menu {\n margin-top: 0;\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {\n margin-bottom: 0;\n border-top-right-radius: 4px;\n border-top-left-radius: 4px;\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n.navbar-btn {\n margin-top: 8px;\n margin-bottom: 8px;\n}\n.navbar-btn.btn-sm {\n margin-top: 10px;\n margin-bottom: 10px;\n}\n.navbar-btn.btn-xs {\n margin-top: 14px;\n margin-bottom: 14px;\n}\n.navbar-text {\n margin-top: 15px;\n margin-bottom: 15px;\n}\n@media (min-width: 768px) {\n .navbar-text {\n float: left;\n margin-left: 15px;\n margin-right: 15px;\n }\n}\n@media (min-width: 768px) {\n .navbar-left {\n float: left !important;\n }\n .navbar-right {\n float: right !important;\n margin-right: -15px;\n }\n .navbar-right ~ .navbar-right {\n margin-right: 0;\n }\n}\n.navbar-default {\n background-color: #f8f8f8;\n border-color: #e7e7e7;\n}\n.navbar-default .navbar-brand {\n color: #777;\n}\n.navbar-default .navbar-brand:hover,\n.navbar-default .navbar-brand:focus {\n color: #5e5e5e;\n background-color: transparent;\n}\n.navbar-default .navbar-text {\n color: #777;\n}\n.navbar-default .navbar-nav > li > a {\n color: #777;\n}\n.navbar-default .navbar-nav > li > a:hover,\n.navbar-default .navbar-nav > li > a:focus {\n color: #333;\n background-color: transparent;\n}\n.navbar-default .navbar-nav > .active > a,\n.navbar-default .navbar-nav > .active > a:hover,\n.navbar-default .navbar-nav > .active > a:focus {\n color: #555;\n background-color: #e7e7e7;\n}\n.navbar-default .navbar-nav > .disabled > a,\n.navbar-default .navbar-nav > .disabled > a:hover,\n.navbar-default .navbar-nav > .disabled > a:focus {\n color: #ccc;\n background-color: transparent;\n}\n.navbar-default .navbar-toggle {\n border-color: #ddd;\n}\n.navbar-default .navbar-toggle:hover,\n.navbar-default .navbar-toggle:focus {\n background-color: #ddd;\n}\n.navbar-default .navbar-toggle .icon-bar {\n background-color: #888;\n}\n.navbar-default .navbar-collapse,\n.navbar-default .navbar-form {\n border-color: #e7e7e7;\n}\n.navbar-default .navbar-nav > .open > a,\n.navbar-default .navbar-nav > .open > a:hover,\n.navbar-default .navbar-nav > .open > a:focus {\n background-color: #e7e7e7;\n color: #555;\n}\n@media (max-width: 767px) {\n .navbar-default .navbar-nav .open .dropdown-menu > li > a {\n color: #777;\n }\n .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover,\n .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus {\n color: #333;\n background-color: transparent;\n }\n .navbar-default .navbar-nav .open .dropdown-menu > .active > a,\n .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover,\n .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus {\n color: #555;\n background-color: #e7e7e7;\n }\n .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a,\n .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover,\n .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus {\n color: #ccc;\n background-color: transparent;\n }\n}\n.navbar-default .navbar-link {\n color: #777;\n}\n.navbar-default .navbar-link:hover {\n color: #333;\n}\n.navbar-default .btn-link {\n color: #777;\n}\n.navbar-default .btn-link:hover,\n.navbar-default .btn-link:focus {\n color: #333;\n}\n.navbar-default .btn-link[disabled]:hover,\nfieldset[disabled] .navbar-default .btn-link:hover,\n.navbar-default .btn-link[disabled]:focus,\nfieldset[disabled] .navbar-default .btn-link:focus {\n color: #ccc;\n}\n.navbar-inverse {\n background-color: #222;\n border-color: #080808;\n}\n.navbar-inverse .navbar-brand {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-brand:hover,\n.navbar-inverse .navbar-brand:focus {\n color: #fff;\n background-color: transparent;\n}\n.navbar-inverse .navbar-text {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-nav > li > a {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-nav > li > a:hover,\n.navbar-inverse .navbar-nav > li > a:focus {\n color: #fff;\n background-color: transparent;\n}\n.navbar-inverse .navbar-nav > .active > a,\n.navbar-inverse .navbar-nav > .active > a:hover,\n.navbar-inverse .navbar-nav > .active > a:focus {\n color: #fff;\n background-color: #080808;\n}\n.navbar-inverse .navbar-nav > .disabled > a,\n.navbar-inverse .navbar-nav > .disabled > a:hover,\n.navbar-inverse .navbar-nav > .disabled > a:focus {\n color: #444;\n background-color: transparent;\n}\n.navbar-inverse .navbar-toggle {\n border-color: #333;\n}\n.navbar-inverse .navbar-toggle:hover,\n.navbar-inverse .navbar-toggle:focus {\n background-color: #333;\n}\n.navbar-inverse .navbar-toggle .icon-bar {\n background-color: #fff;\n}\n.navbar-inverse .navbar-collapse,\n.navbar-inverse .navbar-form {\n border-color: #101010;\n}\n.navbar-inverse .navbar-nav > .open > a,\n.navbar-inverse .navbar-nav > .open > a:hover,\n.navbar-inverse .navbar-nav > .open > a:focus {\n background-color: #080808;\n color: #fff;\n}\n@media (max-width: 767px) {\n .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header {\n border-color: #080808;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu .divider {\n background-color: #080808;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > li > a {\n color: #9d9d9d;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover,\n .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus {\n color: #fff;\n background-color: transparent;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus {\n color: #fff;\n background-color: #080808;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus {\n color: #444;\n background-color: transparent;\n }\n}\n.navbar-inverse .navbar-link {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-link:hover {\n color: #fff;\n}\n.navbar-inverse .btn-link {\n color: #9d9d9d;\n}\n.navbar-inverse .btn-link:hover,\n.navbar-inverse .btn-link:focus {\n color: #fff;\n}\n.navbar-inverse .btn-link[disabled]:hover,\nfieldset[disabled] .navbar-inverse .btn-link:hover,\n.navbar-inverse .btn-link[disabled]:focus,\nfieldset[disabled] .navbar-inverse .btn-link:focus {\n color: #444;\n}\n.breadcrumb {\n padding: 8px 15px;\n margin-bottom: 20px;\n list-style: none;\n background-color: #f5f5f5;\n border-radius: 4px;\n}\n.breadcrumb > li {\n display: inline-block;\n}\n.breadcrumb > li + li:before {\n content: \"/\\00a0\";\n padding: 0 5px;\n color: #ccc;\n}\n.breadcrumb > .active {\n color: #777777;\n}\n.pagination {\n display: inline-block;\n padding-left: 0;\n margin: 20px 0;\n border-radius: 4px;\n}\n.pagination > li {\n display: inline;\n}\n.pagination > li > a,\n.pagination > li > span {\n position: relative;\n float: left;\n padding: 6px 12px;\n line-height: 1.42857143;\n text-decoration: none;\n color: #337ab7;\n background-color: #fff;\n border: 1px solid #ddd;\n margin-left: -1px;\n}\n.pagination > li:first-child > a,\n.pagination > li:first-child > span {\n margin-left: 0;\n border-bottom-left-radius: 4px;\n border-top-left-radius: 4px;\n}\n.pagination > li:last-child > a,\n.pagination > li:last-child > span {\n border-bottom-right-radius: 4px;\n border-top-right-radius: 4px;\n}\n.pagination > li > a:hover,\n.pagination > li > span:hover,\n.pagination > li > a:focus,\n.pagination > li > span:focus {\n z-index: 2;\n color: #23527c;\n background-color: #eeeeee;\n border-color: #ddd;\n}\n.pagination > .active > a,\n.pagination > .active > span,\n.pagination > .active > a:hover,\n.pagination > .active > span:hover,\n.pagination > .active > a:focus,\n.pagination > .active > span:focus {\n z-index: 3;\n color: #fff;\n background-color: #337ab7;\n border-color: #337ab7;\n cursor: default;\n}\n.pagination > .disabled > span,\n.pagination > .disabled > span:hover,\n.pagination > .disabled > span:focus,\n.pagination > .disabled > a,\n.pagination > .disabled > a:hover,\n.pagination > .disabled > a:focus {\n color: #777777;\n background-color: #fff;\n border-color: #ddd;\n cursor: not-allowed;\n}\n.pagination-lg > li > a,\n.pagination-lg > li > span {\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n}\n.pagination-lg > li:first-child > a,\n.pagination-lg > li:first-child > span {\n border-bottom-left-radius: 6px;\n border-top-left-radius: 6px;\n}\n.pagination-lg > li:last-child > a,\n.pagination-lg > li:last-child > span {\n border-bottom-right-radius: 6px;\n border-top-right-radius: 6px;\n}\n.pagination-sm > li > a,\n.pagination-sm > li > span {\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n}\n.pagination-sm > li:first-child > a,\n.pagination-sm > li:first-child > span {\n border-bottom-left-radius: 3px;\n border-top-left-radius: 3px;\n}\n.pagination-sm > li:last-child > a,\n.pagination-sm > li:last-child > span {\n border-bottom-right-radius: 3px;\n border-top-right-radius: 3px;\n}\n.pager {\n padding-left: 0;\n margin: 20px 0;\n list-style: none;\n text-align: center;\n}\n.pager li {\n display: inline;\n}\n.pager li > a,\n.pager li > span {\n display: inline-block;\n padding: 5px 14px;\n background-color: #fff;\n border: 1px solid #ddd;\n border-radius: 15px;\n}\n.pager li > a:hover,\n.pager li > a:focus {\n text-decoration: none;\n background-color: #eeeeee;\n}\n.pager .next > a,\n.pager .next > span {\n float: right;\n}\n.pager .previous > a,\n.pager .previous > span {\n float: left;\n}\n.pager .disabled > a,\n.pager .disabled > a:hover,\n.pager .disabled > a:focus,\n.pager .disabled > span {\n color: #777777;\n background-color: #fff;\n cursor: not-allowed;\n}\n.label {\n display: inline;\n padding: .2em .6em .3em;\n font-size: 75%;\n font-weight: bold;\n line-height: 1;\n color: #fff;\n text-align: center;\n white-space: nowrap;\n vertical-align: baseline;\n border-radius: .25em;\n}\na.label:hover,\na.label:focus {\n color: #fff;\n text-decoration: none;\n cursor: pointer;\n}\n.label:empty {\n display: none;\n}\n.btn .label {\n position: relative;\n top: -1px;\n}\n.label-default {\n background-color: #777777;\n}\n.label-default[href]:hover,\n.label-default[href]:focus {\n background-color: #5e5e5e;\n}\n.label-primary {\n background-color: #337ab7;\n}\n.label-primary[href]:hover,\n.label-primary[href]:focus {\n background-color: #286090;\n}\n.label-success {\n background-color: #5cb85c;\n}\n.label-success[href]:hover,\n.label-success[href]:focus {\n background-color: #449d44;\n}\n.label-info {\n background-color: #5bc0de;\n}\n.label-info[href]:hover,\n.label-info[href]:focus {\n background-color: #31b0d5;\n}\n.label-warning {\n background-color: #f0ad4e;\n}\n.label-warning[href]:hover,\n.label-warning[href]:focus {\n background-color: #ec971f;\n}\n.label-danger {\n background-color: #d9534f;\n}\n.label-danger[href]:hover,\n.label-danger[href]:focus {\n background-color: #c9302c;\n}\n.badge {\n display: inline-block;\n min-width: 10px;\n padding: 3px 7px;\n font-size: 12px;\n font-weight: bold;\n color: #fff;\n line-height: 1;\n vertical-align: middle;\n white-space: nowrap;\n text-align: center;\n background-color: #777777;\n border-radius: 10px;\n}\n.badge:empty {\n display: none;\n}\n.btn .badge {\n position: relative;\n top: -1px;\n}\n.btn-xs .badge,\n.btn-group-xs > .btn .badge {\n top: 0;\n padding: 1px 5px;\n}\na.badge:hover,\na.badge:focus {\n color: #fff;\n text-decoration: none;\n cursor: pointer;\n}\n.list-group-item.active > .badge,\n.nav-pills > .active > a > .badge {\n color: #337ab7;\n background-color: #fff;\n}\n.list-group-item > .badge {\n float: right;\n}\n.list-group-item > .badge + .badge {\n margin-right: 5px;\n}\n.nav-pills > li > a > .badge {\n margin-left: 3px;\n}\n.jumbotron {\n padding-top: 30px;\n padding-bottom: 30px;\n margin-bottom: 30px;\n color: inherit;\n background-color: #eeeeee;\n}\n.jumbotron h1,\n.jumbotron .h1 {\n color: inherit;\n}\n.jumbotron p {\n margin-bottom: 15px;\n font-size: 21px;\n font-weight: 200;\n}\n.jumbotron > hr {\n border-top-color: #d5d5d5;\n}\n.container .jumbotron,\n.container-fluid .jumbotron {\n border-radius: 6px;\n padding-left: 15px;\n padding-right: 15px;\n}\n.jumbotron .container {\n max-width: 100%;\n}\n@media screen and (min-width: 768px) {\n .jumbotron {\n padding-top: 48px;\n padding-bottom: 48px;\n }\n .container .jumbotron,\n .container-fluid .jumbotron {\n padding-left: 60px;\n padding-right: 60px;\n }\n .jumbotron h1,\n .jumbotron .h1 {\n font-size: 63px;\n }\n}\n.thumbnail {\n display: block;\n padding: 4px;\n margin-bottom: 20px;\n line-height: 1.42857143;\n background-color: #fff;\n border: 1px solid #ddd;\n border-radius: 4px;\n -webkit-transition: border 0.2s ease-in-out;\n -o-transition: border 0.2s ease-in-out;\n transition: border 0.2s ease-in-out;\n}\n.thumbnail > img,\n.thumbnail a > img {\n margin-left: auto;\n margin-right: auto;\n}\na.thumbnail:hover,\na.thumbnail:focus,\na.thumbnail.active {\n border-color: #337ab7;\n}\n.thumbnail .caption {\n padding: 9px;\n color: #333333;\n}\n.alert {\n padding: 15px;\n margin-bottom: 20px;\n border: 1px solid transparent;\n border-radius: 4px;\n}\n.alert h4 {\n margin-top: 0;\n color: inherit;\n}\n.alert .alert-link {\n font-weight: bold;\n}\n.alert > p,\n.alert > ul {\n margin-bottom: 0;\n}\n.alert > p + p {\n margin-top: 5px;\n}\n.alert-dismissable,\n.alert-dismissible {\n padding-right: 35px;\n}\n.alert-dismissable .close,\n.alert-dismissible .close {\n position: relative;\n top: -2px;\n right: -21px;\n color: inherit;\n}\n.alert-success {\n background-color: #dff0d8;\n border-color: #d6e9c6;\n color: #3c763d;\n}\n.alert-success hr {\n border-top-color: #c9e2b3;\n}\n.alert-success .alert-link {\n color: #2b542c;\n}\n.alert-info {\n background-color: #d9edf7;\n border-color: #bce8f1;\n color: #31708f;\n}\n.alert-info hr {\n border-top-color: #a6e1ec;\n}\n.alert-info .alert-link {\n color: #245269;\n}\n.alert-warning {\n background-color: #fcf8e3;\n border-color: #faebcc;\n color: #8a6d3b;\n}\n.alert-warning hr {\n border-top-color: #f7e1b5;\n}\n.alert-warning .alert-link {\n color: #66512c;\n}\n.alert-danger {\n background-color: #f2dede;\n border-color: #ebccd1;\n color: #a94442;\n}\n.alert-danger hr {\n border-top-color: #e4b9c0;\n}\n.alert-danger .alert-link {\n color: #843534;\n}\n@-webkit-keyframes progress-bar-stripes {\n from {\n background-position: 40px 0;\n }\n to {\n background-position: 0 0;\n }\n}\n@keyframes progress-bar-stripes {\n from {\n background-position: 40px 0;\n }\n to {\n background-position: 0 0;\n }\n}\n.progress {\n overflow: hidden;\n height: 20px;\n margin-bottom: 20px;\n background-color: #f5f5f5;\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\n box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\n}\n.progress-bar {\n float: left;\n width: 0%;\n height: 100%;\n font-size: 12px;\n line-height: 20px;\n color: #fff;\n text-align: center;\n background-color: #337ab7;\n -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\n box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\n -webkit-transition: width 0.6s ease;\n -o-transition: width 0.6s ease;\n transition: width 0.6s ease;\n}\n.progress-striped .progress-bar,\n.progress-bar-striped {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-size: 40px 40px;\n}\n.progress.active .progress-bar,\n.progress-bar.active {\n -webkit-animation: progress-bar-stripes 2s linear infinite;\n -o-animation: progress-bar-stripes 2s linear infinite;\n animation: progress-bar-stripes 2s linear infinite;\n}\n.progress-bar-success {\n background-color: #5cb85c;\n}\n.progress-striped .progress-bar-success {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.progress-bar-info {\n background-color: #5bc0de;\n}\n.progress-striped .progress-bar-info {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.progress-bar-warning {\n background-color: #f0ad4e;\n}\n.progress-striped .progress-bar-warning {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.progress-bar-danger {\n background-color: #d9534f;\n}\n.progress-striped .progress-bar-danger {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.media {\n margin-top: 15px;\n}\n.media:first-child {\n margin-top: 0;\n}\n.media,\n.media-body {\n zoom: 1;\n overflow: hidden;\n}\n.media-body {\n width: 10000px;\n}\n.media-object {\n display: block;\n}\n.media-object.img-thumbnail {\n max-width: none;\n}\n.media-right,\n.media > .pull-right {\n padding-left: 10px;\n}\n.media-left,\n.media > .pull-left {\n padding-right: 10px;\n}\n.media-left,\n.media-right,\n.media-body {\n display: table-cell;\n vertical-align: top;\n}\n.media-middle {\n vertical-align: middle;\n}\n.media-bottom {\n vertical-align: bottom;\n}\n.media-heading {\n margin-top: 0;\n margin-bottom: 5px;\n}\n.media-list {\n padding-left: 0;\n list-style: none;\n}\n.list-group {\n margin-bottom: 20px;\n padding-left: 0;\n}\n.list-group-item {\n position: relative;\n display: block;\n padding: 10px 15px;\n margin-bottom: -1px;\n background-color: #fff;\n border: 1px solid #ddd;\n}\n.list-group-item:first-child {\n border-top-right-radius: 4px;\n border-top-left-radius: 4px;\n}\n.list-group-item:last-child {\n margin-bottom: 0;\n border-bottom-right-radius: 4px;\n border-bottom-left-radius: 4px;\n}\na.list-group-item,\nbutton.list-group-item {\n color: #555;\n}\na.list-group-item .list-group-item-heading,\nbutton.list-group-item .list-group-item-heading {\n color: #333;\n}\na.list-group-item:hover,\nbutton.list-group-item:hover,\na.list-group-item:focus,\nbutton.list-group-item:focus {\n text-decoration: none;\n color: #555;\n background-color: #f5f5f5;\n}\nbutton.list-group-item {\n width: 100%;\n text-align: left;\n}\n.list-group-item.disabled,\n.list-group-item.disabled:hover,\n.list-group-item.disabled:focus {\n background-color: #eeeeee;\n color: #777777;\n cursor: not-allowed;\n}\n.list-group-item.disabled .list-group-item-heading,\n.list-group-item.disabled:hover .list-group-item-heading,\n.list-group-item.disabled:focus .list-group-item-heading {\n color: inherit;\n}\n.list-group-item.disabled .list-group-item-text,\n.list-group-item.disabled:hover .list-group-item-text,\n.list-group-item.disabled:focus .list-group-item-text {\n color: #777777;\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n z-index: 2;\n color: #fff;\n background-color: #337ab7;\n border-color: #337ab7;\n}\n.list-group-item.active .list-group-item-heading,\n.list-group-item.active:hover .list-group-item-heading,\n.list-group-item.active:focus .list-group-item-heading,\n.list-group-item.active .list-group-item-heading > small,\n.list-group-item.active:hover .list-group-item-heading > small,\n.list-group-item.active:focus .list-group-item-heading > small,\n.list-group-item.active .list-group-item-heading > .small,\n.list-group-item.active:hover .list-group-item-heading > .small,\n.list-group-item.active:focus .list-group-item-heading > .small {\n color: inherit;\n}\n.list-group-item.active .list-group-item-text,\n.list-group-item.active:hover .list-group-item-text,\n.list-group-item.active:focus .list-group-item-text {\n color: #c7ddef;\n}\n.list-group-item-success {\n color: #3c763d;\n background-color: #dff0d8;\n}\na.list-group-item-success,\nbutton.list-group-item-success {\n color: #3c763d;\n}\na.list-group-item-success .list-group-item-heading,\nbutton.list-group-item-success .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-success:hover,\nbutton.list-group-item-success:hover,\na.list-group-item-success:focus,\nbutton.list-group-item-success:focus {\n color: #3c763d;\n background-color: #d0e9c6;\n}\na.list-group-item-success.active,\nbutton.list-group-item-success.active,\na.list-group-item-success.active:hover,\nbutton.list-group-item-success.active:hover,\na.list-group-item-success.active:focus,\nbutton.list-group-item-success.active:focus {\n color: #fff;\n background-color: #3c763d;\n border-color: #3c763d;\n}\n.list-group-item-info {\n color: #31708f;\n background-color: #d9edf7;\n}\na.list-group-item-info,\nbutton.list-group-item-info {\n color: #31708f;\n}\na.list-group-item-info .list-group-item-heading,\nbutton.list-group-item-info .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-info:hover,\nbutton.list-group-item-info:hover,\na.list-group-item-info:focus,\nbutton.list-group-item-info:focus {\n color: #31708f;\n background-color: #c4e3f3;\n}\na.list-group-item-info.active,\nbutton.list-group-item-info.active,\na.list-group-item-info.active:hover,\nbutton.list-group-item-info.active:hover,\na.list-group-item-info.active:focus,\nbutton.list-group-item-info.active:focus {\n color: #fff;\n background-color: #31708f;\n border-color: #31708f;\n}\n.list-group-item-warning {\n color: #8a6d3b;\n background-color: #fcf8e3;\n}\na.list-group-item-warning,\nbutton.list-group-item-warning {\n color: #8a6d3b;\n}\na.list-group-item-warning .list-group-item-heading,\nbutton.list-group-item-warning .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-warning:hover,\nbutton.list-group-item-warning:hover,\na.list-group-item-warning:focus,\nbutton.list-group-item-warning:focus {\n color: #8a6d3b;\n background-color: #faf2cc;\n}\na.list-group-item-warning.active,\nbutton.list-group-item-warning.active,\na.list-group-item-warning.active:hover,\nbutton.list-group-item-warning.active:hover,\na.list-group-item-warning.active:focus,\nbutton.list-group-item-warning.active:focus {\n color: #fff;\n background-color: #8a6d3b;\n border-color: #8a6d3b;\n}\n.list-group-item-danger {\n color: #a94442;\n background-color: #f2dede;\n}\na.list-group-item-danger,\nbutton.list-group-item-danger {\n color: #a94442;\n}\na.list-group-item-danger .list-group-item-heading,\nbutton.list-group-item-danger .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-danger:hover,\nbutton.list-group-item-danger:hover,\na.list-group-item-danger:focus,\nbutton.list-group-item-danger:focus {\n color: #a94442;\n background-color: #ebcccc;\n}\na.list-group-item-danger.active,\nbutton.list-group-item-danger.active,\na.list-group-item-danger.active:hover,\nbutton.list-group-item-danger.active:hover,\na.list-group-item-danger.active:focus,\nbutton.list-group-item-danger.active:focus {\n color: #fff;\n background-color: #a94442;\n border-color: #a94442;\n}\n.list-group-item-heading {\n margin-top: 0;\n margin-bottom: 5px;\n}\n.list-group-item-text {\n margin-bottom: 0;\n line-height: 1.3;\n}\n.panel {\n margin-bottom: 20px;\n background-color: #fff;\n border: 1px solid transparent;\n border-radius: 4px;\n -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);\n box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);\n}\n.panel-body {\n padding: 15px;\n}\n.panel-heading {\n padding: 10px 15px;\n border-bottom: 1px solid transparent;\n border-top-right-radius: 3px;\n border-top-left-radius: 3px;\n}\n.panel-heading > .dropdown .dropdown-toggle {\n color: inherit;\n}\n.panel-title {\n margin-top: 0;\n margin-bottom: 0;\n font-size: 16px;\n color: inherit;\n}\n.panel-title > a,\n.panel-title > small,\n.panel-title > .small,\n.panel-title > small > a,\n.panel-title > .small > a {\n color: inherit;\n}\n.panel-footer {\n padding: 10px 15px;\n background-color: #f5f5f5;\n border-top: 1px solid #ddd;\n border-bottom-right-radius: 3px;\n border-bottom-left-radius: 3px;\n}\n.panel > .list-group,\n.panel > .panel-collapse > .list-group {\n margin-bottom: 0;\n}\n.panel > .list-group .list-group-item,\n.panel > .panel-collapse > .list-group .list-group-item {\n border-width: 1px 0;\n border-radius: 0;\n}\n.panel > .list-group:first-child .list-group-item:first-child,\n.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child {\n border-top: 0;\n border-top-right-radius: 3px;\n border-top-left-radius: 3px;\n}\n.panel > .list-group:last-child .list-group-item:last-child,\n.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child {\n border-bottom: 0;\n border-bottom-right-radius: 3px;\n border-bottom-left-radius: 3px;\n}\n.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child {\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.panel-heading + .list-group .list-group-item:first-child {\n border-top-width: 0;\n}\n.list-group + .panel-footer {\n border-top-width: 0;\n}\n.panel > .table,\n.panel > .table-responsive > .table,\n.panel > .panel-collapse > .table {\n margin-bottom: 0;\n}\n.panel > .table caption,\n.panel > .table-responsive > .table caption,\n.panel > .panel-collapse > .table caption {\n padding-left: 15px;\n padding-right: 15px;\n}\n.panel > .table:first-child,\n.panel > .table-responsive:first-child > .table:first-child {\n border-top-right-radius: 3px;\n border-top-left-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child {\n border-top-left-radius: 3px;\n border-top-right-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child td:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child,\n.panel > .table:first-child > thead:first-child > tr:first-child th:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child {\n border-top-left-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child td:last-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child,\n.panel > .table:first-child > thead:first-child > tr:first-child th:last-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child {\n border-top-right-radius: 3px;\n}\n.panel > .table:last-child,\n.panel > .table-responsive:last-child > .table:last-child {\n border-bottom-right-radius: 3px;\n border-bottom-left-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child {\n border-bottom-left-radius: 3px;\n border-bottom-right-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child,\n.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child {\n border-bottom-left-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child,\n.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child {\n border-bottom-right-radius: 3px;\n}\n.panel > .panel-body + .table,\n.panel > .panel-body + .table-responsive,\n.panel > .table + .panel-body,\n.panel > .table-responsive + .panel-body {\n border-top: 1px solid #ddd;\n}\n.panel > .table > tbody:first-child > tr:first-child th,\n.panel > .table > tbody:first-child > tr:first-child td {\n border-top: 0;\n}\n.panel > .table-bordered,\n.panel > .table-responsive > .table-bordered {\n border: 0;\n}\n.panel > .table-bordered > thead > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > thead > tr > th:first-child,\n.panel > .table-bordered > tbody > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child,\n.panel > .table-bordered > tfoot > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child,\n.panel > .table-bordered > thead > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > thead > tr > td:first-child,\n.panel > .table-bordered > tbody > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child,\n.panel > .table-bordered > tfoot > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child {\n border-left: 0;\n}\n.panel > .table-bordered > thead > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > thead > tr > th:last-child,\n.panel > .table-bordered > tbody > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child,\n.panel > .table-bordered > tfoot > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child,\n.panel > .table-bordered > thead > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > thead > tr > td:last-child,\n.panel > .table-bordered > tbody > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child,\n.panel > .table-bordered > tfoot > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child {\n border-right: 0;\n}\n.panel > .table-bordered > thead > tr:first-child > td,\n.panel > .table-responsive > .table-bordered > thead > tr:first-child > td,\n.panel > .table-bordered > tbody > tr:first-child > td,\n.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td,\n.panel > .table-bordered > thead > tr:first-child > th,\n.panel > .table-responsive > .table-bordered > thead > tr:first-child > th,\n.panel > .table-bordered > tbody > tr:first-child > th,\n.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th {\n border-bottom: 0;\n}\n.panel > .table-bordered > tbody > tr:last-child > td,\n.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td,\n.panel > .table-bordered > tfoot > tr:last-child > td,\n.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td,\n.panel > .table-bordered > tbody > tr:last-child > th,\n.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th,\n.panel > .table-bordered > tfoot > tr:last-child > th,\n.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th {\n border-bottom: 0;\n}\n.panel > .table-responsive {\n border: 0;\n margin-bottom: 0;\n}\n.panel-group {\n margin-bottom: 20px;\n}\n.panel-group .panel {\n margin-bottom: 0;\n border-radius: 4px;\n}\n.panel-group .panel + .panel {\n margin-top: 5px;\n}\n.panel-group .panel-heading {\n border-bottom: 0;\n}\n.panel-group .panel-heading + .panel-collapse > .panel-body,\n.panel-group .panel-heading + .panel-collapse > .list-group {\n border-top: 1px solid #ddd;\n}\n.panel-group .panel-footer {\n border-top: 0;\n}\n.panel-group .panel-footer + .panel-collapse .panel-body {\n border-bottom: 1px solid #ddd;\n}\n.panel-default {\n border-color: #ddd;\n}\n.panel-default > .panel-heading {\n color: #333333;\n background-color: #f5f5f5;\n border-color: #ddd;\n}\n.panel-default > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #ddd;\n}\n.panel-default > .panel-heading .badge {\n color: #f5f5f5;\n background-color: #333333;\n}\n.panel-default > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #ddd;\n}\n.panel-primary {\n border-color: #337ab7;\n}\n.panel-primary > .panel-heading {\n color: #fff;\n background-color: #337ab7;\n border-color: #337ab7;\n}\n.panel-primary > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #337ab7;\n}\n.panel-primary > .panel-heading .badge {\n color: #337ab7;\n background-color: #fff;\n}\n.panel-primary > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #337ab7;\n}\n.panel-success {\n border-color: #d6e9c6;\n}\n.panel-success > .panel-heading {\n color: #3c763d;\n background-color: #dff0d8;\n border-color: #d6e9c6;\n}\n.panel-success > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #d6e9c6;\n}\n.panel-success > .panel-heading .badge {\n color: #dff0d8;\n background-color: #3c763d;\n}\n.panel-success > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #d6e9c6;\n}\n.panel-info {\n border-color: #bce8f1;\n}\n.panel-info > .panel-heading {\n color: #31708f;\n background-color: #d9edf7;\n border-color: #bce8f1;\n}\n.panel-info > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #bce8f1;\n}\n.panel-info > .panel-heading .badge {\n color: #d9edf7;\n background-color: #31708f;\n}\n.panel-info > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #bce8f1;\n}\n.panel-warning {\n border-color: #faebcc;\n}\n.panel-warning > .panel-heading {\n color: #8a6d3b;\n background-color: #fcf8e3;\n border-color: #faebcc;\n}\n.panel-warning > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #faebcc;\n}\n.panel-warning > .panel-heading .badge {\n color: #fcf8e3;\n background-color: #8a6d3b;\n}\n.panel-warning > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #faebcc;\n}\n.panel-danger {\n border-color: #ebccd1;\n}\n.panel-danger > .panel-heading {\n color: #a94442;\n background-color: #f2dede;\n border-color: #ebccd1;\n}\n.panel-danger > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #ebccd1;\n}\n.panel-danger > .panel-heading .badge {\n color: #f2dede;\n background-color: #a94442;\n}\n.panel-danger > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #ebccd1;\n}\n.embed-responsive {\n position: relative;\n display: block;\n height: 0;\n padding: 0;\n overflow: hidden;\n}\n.embed-responsive .embed-responsive-item,\n.embed-responsive iframe,\n.embed-responsive embed,\n.embed-responsive object,\n.embed-responsive video {\n position: absolute;\n top: 0;\n left: 0;\n bottom: 0;\n height: 100%;\n width: 100%;\n border: 0;\n}\n.embed-responsive-16by9 {\n padding-bottom: 56.25%;\n}\n.embed-responsive-4by3 {\n padding-bottom: 75%;\n}\n.well {\n min-height: 20px;\n padding: 19px;\n margin-bottom: 20px;\n background-color: #f5f5f5;\n border: 1px solid #e3e3e3;\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n}\n.well blockquote {\n border-color: #ddd;\n border-color: rgba(0, 0, 0, 0.15);\n}\n.well-lg {\n padding: 24px;\n border-radius: 6px;\n}\n.well-sm {\n padding: 9px;\n border-radius: 3px;\n}\n.close {\n float: right;\n font-size: 21px;\n font-weight: bold;\n line-height: 1;\n color: #000;\n text-shadow: 0 1px 0 #fff;\n opacity: 0.2;\n filter: alpha(opacity=20);\n}\n.close:hover,\n.close:focus {\n color: #000;\n text-decoration: none;\n cursor: pointer;\n opacity: 0.5;\n filter: alpha(opacity=50);\n}\nbutton.close {\n padding: 0;\n cursor: pointer;\n background: transparent;\n border: 0;\n -webkit-appearance: none;\n}\n.modal-open {\n overflow: hidden;\n}\n.modal {\n display: none;\n overflow: hidden;\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1050;\n -webkit-overflow-scrolling: touch;\n outline: 0;\n}\n.modal.fade .modal-dialog {\n -webkit-transform: translate(0, -25%);\n -ms-transform: translate(0, -25%);\n -o-transform: translate(0, -25%);\n transform: translate(0, -25%);\n -webkit-transition: -webkit-transform 0.3s ease-out;\n -moz-transition: -moz-transform 0.3s ease-out;\n -o-transition: -o-transform 0.3s ease-out;\n transition: transform 0.3s ease-out;\n}\n.modal.in .modal-dialog {\n -webkit-transform: translate(0, 0);\n -ms-transform: translate(0, 0);\n -o-transform: translate(0, 0);\n transform: translate(0, 0);\n}\n.modal-open .modal {\n overflow-x: hidden;\n overflow-y: auto;\n}\n.modal-dialog {\n position: relative;\n width: auto;\n margin: 10px;\n}\n.modal-content {\n position: relative;\n background-color: #fff;\n border: 1px solid #999;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 6px;\n -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);\n box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);\n background-clip: padding-box;\n outline: 0;\n}\n.modal-backdrop {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1040;\n background-color: #000;\n}\n.modal-backdrop.fade {\n opacity: 0;\n filter: alpha(opacity=0);\n}\n.modal-backdrop.in {\n opacity: 0.5;\n filter: alpha(opacity=50);\n}\n.modal-header {\n padding: 15px;\n border-bottom: 1px solid #e5e5e5;\n}\n.modal-header .close {\n margin-top: -2px;\n}\n.modal-title {\n margin: 0;\n line-height: 1.42857143;\n}\n.modal-body {\n position: relative;\n padding: 15px;\n}\n.modal-footer {\n padding: 15px;\n text-align: right;\n border-top: 1px solid #e5e5e5;\n}\n.modal-footer .btn + .btn {\n margin-left: 5px;\n margin-bottom: 0;\n}\n.modal-footer .btn-group .btn + .btn {\n margin-left: -1px;\n}\n.modal-footer .btn-block + .btn-block {\n margin-left: 0;\n}\n.modal-scrollbar-measure {\n position: absolute;\n top: -9999px;\n width: 50px;\n height: 50px;\n overflow: scroll;\n}\n@media (min-width: 768px) {\n .modal-dialog {\n width: 600px;\n margin: 30px auto;\n }\n .modal-content {\n -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);\n box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);\n }\n .modal-sm {\n width: 300px;\n }\n}\n@media (min-width: 992px) {\n .modal-lg {\n width: 900px;\n }\n}\n.tooltip {\n position: absolute;\n z-index: 1070;\n display: block;\n font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n font-style: normal;\n font-weight: normal;\n letter-spacing: normal;\n line-break: auto;\n line-height: 1.42857143;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n white-space: normal;\n word-break: normal;\n word-spacing: normal;\n word-wrap: normal;\n font-size: 12px;\n opacity: 0;\n filter: alpha(opacity=0);\n}\n.tooltip.in {\n opacity: 0.9;\n filter: alpha(opacity=90);\n}\n.tooltip.top {\n margin-top: -3px;\n padding: 5px 0;\n}\n.tooltip.right {\n margin-left: 3px;\n padding: 0 5px;\n}\n.tooltip.bottom {\n margin-top: 3px;\n padding: 5px 0;\n}\n.tooltip.left {\n margin-left: -3px;\n padding: 0 5px;\n}\n.tooltip-inner {\n max-width: 200px;\n padding: 3px 8px;\n color: #fff;\n text-align: center;\n background-color: #000;\n border-radius: 4px;\n}\n.tooltip-arrow {\n position: absolute;\n width: 0;\n height: 0;\n border-color: transparent;\n border-style: solid;\n}\n.tooltip.top .tooltip-arrow {\n bottom: 0;\n left: 50%;\n margin-left: -5px;\n border-width: 5px 5px 0;\n border-top-color: #000;\n}\n.tooltip.top-left .tooltip-arrow {\n bottom: 0;\n right: 5px;\n margin-bottom: -5px;\n border-width: 5px 5px 0;\n border-top-color: #000;\n}\n.tooltip.top-right .tooltip-arrow {\n bottom: 0;\n left: 5px;\n margin-bottom: -5px;\n border-width: 5px 5px 0;\n border-top-color: #000;\n}\n.tooltip.right .tooltip-arrow {\n top: 50%;\n left: 0;\n margin-top: -5px;\n border-width: 5px 5px 5px 0;\n border-right-color: #000;\n}\n.tooltip.left .tooltip-arrow {\n top: 50%;\n right: 0;\n margin-top: -5px;\n border-width: 5px 0 5px 5px;\n border-left-color: #000;\n}\n.tooltip.bottom .tooltip-arrow {\n top: 0;\n left: 50%;\n margin-left: -5px;\n border-width: 0 5px 5px;\n border-bottom-color: #000;\n}\n.tooltip.bottom-left .tooltip-arrow {\n top: 0;\n right: 5px;\n margin-top: -5px;\n border-width: 0 5px 5px;\n border-bottom-color: #000;\n}\n.tooltip.bottom-right .tooltip-arrow {\n top: 0;\n left: 5px;\n margin-top: -5px;\n border-width: 0 5px 5px;\n border-bottom-color: #000;\n}\n.popover {\n position: absolute;\n top: 0;\n left: 0;\n z-index: 1060;\n display: none;\n max-width: 276px;\n padding: 1px;\n font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n font-style: normal;\n font-weight: normal;\n letter-spacing: normal;\n line-break: auto;\n line-height: 1.42857143;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n white-space: normal;\n word-break: normal;\n word-spacing: normal;\n word-wrap: normal;\n font-size: 14px;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid #ccc;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 6px;\n -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n}\n.popover.top {\n margin-top: -10px;\n}\n.popover.right {\n margin-left: 10px;\n}\n.popover.bottom {\n margin-top: 10px;\n}\n.popover.left {\n margin-left: -10px;\n}\n.popover-title {\n margin: 0;\n padding: 8px 14px;\n font-size: 14px;\n background-color: #f7f7f7;\n border-bottom: 1px solid #ebebeb;\n border-radius: 5px 5px 0 0;\n}\n.popover-content {\n padding: 9px 14px;\n}\n.popover > .arrow,\n.popover > .arrow:after {\n position: absolute;\n display: block;\n width: 0;\n height: 0;\n border-color: transparent;\n border-style: solid;\n}\n.popover > .arrow {\n border-width: 11px;\n}\n.popover > .arrow:after {\n border-width: 10px;\n content: \"\";\n}\n.popover.top > .arrow {\n left: 50%;\n margin-left: -11px;\n border-bottom-width: 0;\n border-top-color: #999999;\n border-top-color: rgba(0, 0, 0, 0.25);\n bottom: -11px;\n}\n.popover.top > .arrow:after {\n content: \" \";\n bottom: 1px;\n margin-left: -10px;\n border-bottom-width: 0;\n border-top-color: #fff;\n}\n.popover.right > .arrow {\n top: 50%;\n left: -11px;\n margin-top: -11px;\n border-left-width: 0;\n border-right-color: #999999;\n border-right-color: rgba(0, 0, 0, 0.25);\n}\n.popover.right > .arrow:after {\n content: \" \";\n left: 1px;\n bottom: -10px;\n border-left-width: 0;\n border-right-color: #fff;\n}\n.popover.bottom > .arrow {\n left: 50%;\n margin-left: -11px;\n border-top-width: 0;\n border-bottom-color: #999999;\n border-bottom-color: rgba(0, 0, 0, 0.25);\n top: -11px;\n}\n.popover.bottom > .arrow:after {\n content: \" \";\n top: 1px;\n margin-left: -10px;\n border-top-width: 0;\n border-bottom-color: #fff;\n}\n.popover.left > .arrow {\n top: 50%;\n right: -11px;\n margin-top: -11px;\n border-right-width: 0;\n border-left-color: #999999;\n border-left-color: rgba(0, 0, 0, 0.25);\n}\n.popover.left > .arrow:after {\n content: \" \";\n right: 1px;\n border-right-width: 0;\n border-left-color: #fff;\n bottom: -10px;\n}\n.carousel {\n position: relative;\n}\n.carousel-inner {\n position: relative;\n overflow: hidden;\n width: 100%;\n}\n.carousel-inner > .item {\n display: none;\n position: relative;\n -webkit-transition: 0.6s ease-in-out left;\n -o-transition: 0.6s ease-in-out left;\n transition: 0.6s ease-in-out left;\n}\n.carousel-inner > .item > img,\n.carousel-inner > .item > a > img {\n line-height: 1;\n}\n@media all and (transform-3d), (-webkit-transform-3d) {\n .carousel-inner > .item {\n -webkit-transition: -webkit-transform 0.6s ease-in-out;\n -moz-transition: -moz-transform 0.6s ease-in-out;\n -o-transition: -o-transform 0.6s ease-in-out;\n transition: transform 0.6s ease-in-out;\n -webkit-backface-visibility: hidden;\n -moz-backface-visibility: hidden;\n backface-visibility: hidden;\n -webkit-perspective: 1000px;\n -moz-perspective: 1000px;\n perspective: 1000px;\n }\n .carousel-inner > .item.next,\n .carousel-inner > .item.active.right {\n -webkit-transform: translate3d(100%, 0, 0);\n transform: translate3d(100%, 0, 0);\n left: 0;\n }\n .carousel-inner > .item.prev,\n .carousel-inner > .item.active.left {\n -webkit-transform: translate3d(-100%, 0, 0);\n transform: translate3d(-100%, 0, 0);\n left: 0;\n }\n .carousel-inner > .item.next.left,\n .carousel-inner > .item.prev.right,\n .carousel-inner > .item.active {\n -webkit-transform: translate3d(0, 0, 0);\n transform: translate3d(0, 0, 0);\n left: 0;\n }\n}\n.carousel-inner > .active,\n.carousel-inner > .next,\n.carousel-inner > .prev {\n display: block;\n}\n.carousel-inner > .active {\n left: 0;\n}\n.carousel-inner > .next,\n.carousel-inner > .prev {\n position: absolute;\n top: 0;\n width: 100%;\n}\n.carousel-inner > .next {\n left: 100%;\n}\n.carousel-inner > .prev {\n left: -100%;\n}\n.carousel-inner > .next.left,\n.carousel-inner > .prev.right {\n left: 0;\n}\n.carousel-inner > .active.left {\n left: -100%;\n}\n.carousel-inner > .active.right {\n left: 100%;\n}\n.carousel-control {\n position: absolute;\n top: 0;\n left: 0;\n bottom: 0;\n width: 15%;\n opacity: 0.5;\n filter: alpha(opacity=50);\n font-size: 20px;\n color: #fff;\n text-align: center;\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\n background-color: rgba(0, 0, 0, 0);\n}\n.carousel-control.left {\n background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);\n}\n.carousel-control.right {\n left: auto;\n right: 0;\n background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);\n}\n.carousel-control:hover,\n.carousel-control:focus {\n outline: 0;\n color: #fff;\n text-decoration: none;\n opacity: 0.9;\n filter: alpha(opacity=90);\n}\n.carousel-control .icon-prev,\n.carousel-control .icon-next,\n.carousel-control .glyphicon-chevron-left,\n.carousel-control .glyphicon-chevron-right {\n position: absolute;\n top: 50%;\n margin-top: -10px;\n z-index: 5;\n display: inline-block;\n}\n.carousel-control .icon-prev,\n.carousel-control .glyphicon-chevron-left {\n left: 50%;\n margin-left: -10px;\n}\n.carousel-control .icon-next,\n.carousel-control .glyphicon-chevron-right {\n right: 50%;\n margin-right: -10px;\n}\n.carousel-control .icon-prev,\n.carousel-control .icon-next {\n width: 20px;\n height: 20px;\n line-height: 1;\n font-family: serif;\n}\n.carousel-control .icon-prev:before {\n content: '\\2039';\n}\n.carousel-control .icon-next:before {\n content: '\\203a';\n}\n.carousel-indicators {\n position: absolute;\n bottom: 10px;\n left: 50%;\n z-index: 15;\n width: 60%;\n margin-left: -30%;\n padding-left: 0;\n list-style: none;\n text-align: center;\n}\n.carousel-indicators li {\n display: inline-block;\n width: 10px;\n height: 10px;\n margin: 1px;\n text-indent: -999px;\n border: 1px solid #fff;\n border-radius: 10px;\n cursor: pointer;\n background-color: #000 \\9;\n background-color: rgba(0, 0, 0, 0);\n}\n.carousel-indicators .active {\n margin: 0;\n width: 12px;\n height: 12px;\n background-color: #fff;\n}\n.carousel-caption {\n position: absolute;\n left: 15%;\n right: 15%;\n bottom: 20px;\n z-index: 10;\n padding-top: 20px;\n padding-bottom: 20px;\n color: #fff;\n text-align: center;\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\n}\n.carousel-caption .btn {\n text-shadow: none;\n}\n@media screen and (min-width: 768px) {\n .carousel-control .glyphicon-chevron-left,\n .carousel-control .glyphicon-chevron-right,\n .carousel-control .icon-prev,\n .carousel-control .icon-next {\n width: 30px;\n height: 30px;\n margin-top: -10px;\n font-size: 30px;\n }\n .carousel-control .glyphicon-chevron-left,\n .carousel-control .icon-prev {\n margin-left: -10px;\n }\n .carousel-control .glyphicon-chevron-right,\n .carousel-control .icon-next {\n margin-right: -10px;\n }\n .carousel-caption {\n left: 20%;\n right: 20%;\n padding-bottom: 30px;\n }\n .carousel-indicators {\n bottom: 20px;\n }\n}\n.clearfix:before,\n.clearfix:after,\n.dl-horizontal dd:before,\n.dl-horizontal dd:after,\n.container:before,\n.container:after,\n.container-fluid:before,\n.container-fluid:after,\n.row:before,\n.row:after,\n.form-horizontal .form-group:before,\n.form-horizontal .form-group:after,\n.btn-toolbar:before,\n.btn-toolbar:after,\n.btn-group-vertical > .btn-group:before,\n.btn-group-vertical > .btn-group:after,\n.nav:before,\n.nav:after,\n.navbar:before,\n.navbar:after,\n.navbar-header:before,\n.navbar-header:after,\n.navbar-collapse:before,\n.navbar-collapse:after,\n.pager:before,\n.pager:after,\n.panel-body:before,\n.panel-body:after,\n.modal-header:before,\n.modal-header:after,\n.modal-footer:before,\n.modal-footer:after {\n content: \" \";\n display: table;\n}\n.clearfix:after,\n.dl-horizontal dd:after,\n.container:after,\n.container-fluid:after,\n.row:after,\n.form-horizontal .form-group:after,\n.btn-toolbar:after,\n.btn-group-vertical > .btn-group:after,\n.nav:after,\n.navbar:after,\n.navbar-header:after,\n.navbar-collapse:after,\n.pager:after,\n.panel-body:after,\n.modal-header:after,\n.modal-footer:after {\n clear: both;\n}\n.center-block {\n display: block;\n margin-left: auto;\n margin-right: auto;\n}\n.pull-right {\n float: right !important;\n}\n.pull-left {\n float: left !important;\n}\n.hide {\n display: none !important;\n}\n.show {\n display: block !important;\n}\n.invisible {\n visibility: hidden;\n}\n.text-hide {\n font: 0/0 a;\n color: transparent;\n text-shadow: none;\n background-color: transparent;\n border: 0;\n}\n.hidden {\n display: none !important;\n}\n.affix {\n position: fixed;\n}\n@-ms-viewport {\n width: device-width;\n}\n.visible-xs,\n.visible-sm,\n.visible-md,\n.visible-lg {\n display: none !important;\n}\n.visible-xs-block,\n.visible-xs-inline,\n.visible-xs-inline-block,\n.visible-sm-block,\n.visible-sm-inline,\n.visible-sm-inline-block,\n.visible-md-block,\n.visible-md-inline,\n.visible-md-inline-block,\n.visible-lg-block,\n.visible-lg-inline,\n.visible-lg-inline-block {\n display: none !important;\n}\n@media (max-width: 767px) {\n .visible-xs {\n display: block !important;\n }\n table.visible-xs {\n display: table !important;\n }\n tr.visible-xs {\n display: table-row !important;\n }\n th.visible-xs,\n td.visible-xs {\n display: table-cell !important;\n }\n}\n@media (max-width: 767px) {\n .visible-xs-block {\n display: block !important;\n }\n}\n@media (max-width: 767px) {\n .visible-xs-inline {\n display: inline !important;\n }\n}\n@media (max-width: 767px) {\n .visible-xs-inline-block {\n display: inline-block !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm {\n display: block !important;\n }\n table.visible-sm {\n display: table !important;\n }\n tr.visible-sm {\n display: table-row !important;\n }\n th.visible-sm,\n td.visible-sm {\n display: table-cell !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm-block {\n display: block !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm-inline {\n display: inline !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm-inline-block {\n display: inline-block !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md {\n display: block !important;\n }\n table.visible-md {\n display: table !important;\n }\n tr.visible-md {\n display: table-row !important;\n }\n th.visible-md,\n td.visible-md {\n display: table-cell !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md-block {\n display: block !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md-inline {\n display: inline !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md-inline-block {\n display: inline-block !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg {\n display: block !important;\n }\n table.visible-lg {\n display: table !important;\n }\n tr.visible-lg {\n display: table-row !important;\n }\n th.visible-lg,\n td.visible-lg {\n display: table-cell !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg-block {\n display: block !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg-inline {\n display: inline !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg-inline-block {\n display: inline-block !important;\n }\n}\n@media (max-width: 767px) {\n .hidden-xs {\n display: none !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .hidden-sm {\n display: none !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .hidden-md {\n display: none !important;\n }\n}\n@media (min-width: 1200px) {\n .hidden-lg {\n display: none !important;\n }\n}\n.visible-print {\n display: none !important;\n}\n@media print {\n .visible-print {\n display: block !important;\n }\n table.visible-print {\n display: table !important;\n }\n tr.visible-print {\n display: table-row !important;\n }\n th.visible-print,\n td.visible-print {\n display: table-cell !important;\n }\n}\n.visible-print-block {\n display: none !important;\n}\n@media print {\n .visible-print-block {\n display: block !important;\n }\n}\n.visible-print-inline {\n display: none !important;\n}\n@media print {\n .visible-print-inline {\n display: inline !important;\n }\n}\n.visible-print-inline-block {\n display: none !important;\n}\n@media print {\n .visible-print-inline-block {\n display: inline-block !important;\n }\n}\n@media print {\n .hidden-print {\n display: none !important;\n }\n}\n/*# sourceMappingURL=bootstrap.css.map */","/*!\n * Bootstrap v3.3.7 (http://getbootstrap.com)\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */\nhtml {\n font-family: sans-serif;\n -webkit-text-size-adjust: 100%;\n -ms-text-size-adjust: 100%;\n}\nbody {\n margin: 0;\n}\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nmenu,\nnav,\nsection,\nsummary {\n display: block;\n}\naudio,\ncanvas,\nprogress,\nvideo {\n display: inline-block;\n vertical-align: baseline;\n}\naudio:not([controls]) {\n display: none;\n height: 0;\n}\n[hidden],\ntemplate {\n display: none;\n}\na {\n background-color: transparent;\n}\na:active,\na:hover {\n outline: 0;\n}\nabbr[title] {\n border-bottom: 1px dotted;\n}\nb,\nstrong {\n font-weight: bold;\n}\ndfn {\n font-style: italic;\n}\nh1 {\n margin: .67em 0;\n font-size: 2em;\n}\nmark {\n color: #000;\n background: #ff0;\n}\nsmall {\n font-size: 80%;\n}\nsub,\nsup {\n position: relative;\n font-size: 75%;\n line-height: 0;\n vertical-align: baseline;\n}\nsup {\n top: -.5em;\n}\nsub {\n bottom: -.25em;\n}\nimg {\n border: 0;\n}\nsvg:not(:root) {\n overflow: hidden;\n}\nfigure {\n margin: 1em 40px;\n}\nhr {\n height: 0;\n -webkit-box-sizing: content-box;\n -moz-box-sizing: content-box;\n box-sizing: content-box;\n}\npre {\n overflow: auto;\n}\ncode,\nkbd,\npre,\nsamp {\n font-family: monospace, monospace;\n font-size: 1em;\n}\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n margin: 0;\n font: inherit;\n color: inherit;\n}\nbutton {\n overflow: visible;\n}\nbutton,\nselect {\n text-transform: none;\n}\nbutton,\nhtml input[type=\"button\"],\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n -webkit-appearance: button;\n cursor: pointer;\n}\nbutton[disabled],\nhtml input[disabled] {\n cursor: default;\n}\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n padding: 0;\n border: 0;\n}\ninput {\n line-height: normal;\n}\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n padding: 0;\n}\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\ninput[type=\"search\"] {\n -webkit-box-sizing: content-box;\n -moz-box-sizing: content-box;\n box-sizing: content-box;\n -webkit-appearance: textfield;\n}\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\nfieldset {\n padding: .35em .625em .75em;\n margin: 0 2px;\n border: 1px solid #c0c0c0;\n}\nlegend {\n padding: 0;\n border: 0;\n}\ntextarea {\n overflow: auto;\n}\noptgroup {\n font-weight: bold;\n}\ntable {\n border-spacing: 0;\n border-collapse: collapse;\n}\ntd,\nth {\n padding: 0;\n}\n/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */\n@media print {\n *,\n *:before,\n *:after {\n color: #000 !important;\n text-shadow: none !important;\n background: transparent !important;\n -webkit-box-shadow: none !important;\n box-shadow: none !important;\n }\n a,\n a:visited {\n text-decoration: underline;\n }\n a[href]:after {\n content: \" (\" attr(href) \")\";\n }\n abbr[title]:after {\n content: \" (\" attr(title) \")\";\n }\n a[href^=\"#\"]:after,\n a[href^=\"javascript:\"]:after {\n content: \"\";\n }\n pre,\n blockquote {\n border: 1px solid #999;\n\n page-break-inside: avoid;\n }\n thead {\n display: table-header-group;\n }\n tr,\n img {\n page-break-inside: avoid;\n }\n img {\n max-width: 100% !important;\n }\n p,\n h2,\n h3 {\n orphans: 3;\n widows: 3;\n }\n h2,\n h3 {\n page-break-after: avoid;\n }\n .navbar {\n display: none;\n }\n .btn > .caret,\n .dropup > .btn > .caret {\n border-top-color: #000 !important;\n }\n .label {\n border: 1px solid #000;\n }\n .table {\n border-collapse: collapse !important;\n }\n .table td,\n .table th {\n background-color: #fff !important;\n }\n .table-bordered th,\n .table-bordered td {\n border: 1px solid #ddd !important;\n }\n}\n@font-face {\n font-family: 'Glyphicons Halflings';\n\n src: url('../fonts/glyphicons-halflings-regular.eot');\n src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');\n}\n.glyphicon {\n position: relative;\n top: 1px;\n display: inline-block;\n font-family: 'Glyphicons Halflings';\n font-style: normal;\n font-weight: normal;\n line-height: 1;\n\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n.glyphicon-asterisk:before {\n content: \"\\002a\";\n}\n.glyphicon-plus:before {\n content: \"\\002b\";\n}\n.glyphicon-euro:before,\n.glyphicon-eur:before {\n content: \"\\20ac\";\n}\n.glyphicon-minus:before {\n content: \"\\2212\";\n}\n.glyphicon-cloud:before {\n content: \"\\2601\";\n}\n.glyphicon-envelope:before {\n content: \"\\2709\";\n}\n.glyphicon-pencil:before {\n content: \"\\270f\";\n}\n.glyphicon-glass:before {\n content: \"\\e001\";\n}\n.glyphicon-music:before {\n content: \"\\e002\";\n}\n.glyphicon-search:before {\n content: \"\\e003\";\n}\n.glyphicon-heart:before {\n content: \"\\e005\";\n}\n.glyphicon-star:before {\n content: \"\\e006\";\n}\n.glyphicon-star-empty:before {\n content: \"\\e007\";\n}\n.glyphicon-user:before {\n content: \"\\e008\";\n}\n.glyphicon-film:before {\n content: \"\\e009\";\n}\n.glyphicon-th-large:before {\n content: \"\\e010\";\n}\n.glyphicon-th:before {\n content: \"\\e011\";\n}\n.glyphicon-th-list:before {\n content: \"\\e012\";\n}\n.glyphicon-ok:before {\n content: \"\\e013\";\n}\n.glyphicon-remove:before {\n content: \"\\e014\";\n}\n.glyphicon-zoom-in:before {\n content: \"\\e015\";\n}\n.glyphicon-zoom-out:before {\n content: \"\\e016\";\n}\n.glyphicon-off:before {\n content: \"\\e017\";\n}\n.glyphicon-signal:before {\n content: \"\\e018\";\n}\n.glyphicon-cog:before {\n content: \"\\e019\";\n}\n.glyphicon-trash:before {\n content: \"\\e020\";\n}\n.glyphicon-home:before {\n content: \"\\e021\";\n}\n.glyphicon-file:before {\n content: \"\\e022\";\n}\n.glyphicon-time:before {\n content: \"\\e023\";\n}\n.glyphicon-road:before {\n content: \"\\e024\";\n}\n.glyphicon-download-alt:before {\n content: \"\\e025\";\n}\n.glyphicon-download:before {\n content: \"\\e026\";\n}\n.glyphicon-upload:before {\n content: \"\\e027\";\n}\n.glyphicon-inbox:before {\n content: \"\\e028\";\n}\n.glyphicon-play-circle:before {\n content: \"\\e029\";\n}\n.glyphicon-repeat:before {\n content: \"\\e030\";\n}\n.glyphicon-refresh:before {\n content: \"\\e031\";\n}\n.glyphicon-list-alt:before {\n content: \"\\e032\";\n}\n.glyphicon-lock:before {\n content: \"\\e033\";\n}\n.glyphicon-flag:before {\n content: \"\\e034\";\n}\n.glyphicon-headphones:before {\n content: \"\\e035\";\n}\n.glyphicon-volume-off:before {\n content: \"\\e036\";\n}\n.glyphicon-volume-down:before {\n content: \"\\e037\";\n}\n.glyphicon-volume-up:before {\n content: \"\\e038\";\n}\n.glyphicon-qrcode:before {\n content: \"\\e039\";\n}\n.glyphicon-barcode:before {\n content: \"\\e040\";\n}\n.glyphicon-tag:before {\n content: \"\\e041\";\n}\n.glyphicon-tags:before {\n content: \"\\e042\";\n}\n.glyphicon-book:before {\n content: \"\\e043\";\n}\n.glyphicon-bookmark:before {\n content: \"\\e044\";\n}\n.glyphicon-print:before {\n content: \"\\e045\";\n}\n.glyphicon-camera:before {\n content: \"\\e046\";\n}\n.glyphicon-font:before {\n content: \"\\e047\";\n}\n.glyphicon-bold:before {\n content: \"\\e048\";\n}\n.glyphicon-italic:before {\n content: \"\\e049\";\n}\n.glyphicon-text-height:before {\n content: \"\\e050\";\n}\n.glyphicon-text-width:before {\n content: \"\\e051\";\n}\n.glyphicon-align-left:before {\n content: \"\\e052\";\n}\n.glyphicon-align-center:before {\n content: \"\\e053\";\n}\n.glyphicon-align-right:before {\n content: \"\\e054\";\n}\n.glyphicon-align-justify:before {\n content: \"\\e055\";\n}\n.glyphicon-list:before {\n content: \"\\e056\";\n}\n.glyphicon-indent-left:before {\n content: \"\\e057\";\n}\n.glyphicon-indent-right:before {\n content: \"\\e058\";\n}\n.glyphicon-facetime-video:before {\n content: \"\\e059\";\n}\n.glyphicon-picture:before {\n content: \"\\e060\";\n}\n.glyphicon-map-marker:before {\n content: \"\\e062\";\n}\n.glyphicon-adjust:before {\n content: \"\\e063\";\n}\n.glyphicon-tint:before {\n content: \"\\e064\";\n}\n.glyphicon-edit:before {\n content: \"\\e065\";\n}\n.glyphicon-share:before {\n content: \"\\e066\";\n}\n.glyphicon-check:before {\n content: \"\\e067\";\n}\n.glyphicon-move:before {\n content: \"\\e068\";\n}\n.glyphicon-step-backward:before {\n content: \"\\e069\";\n}\n.glyphicon-fast-backward:before {\n content: \"\\e070\";\n}\n.glyphicon-backward:before {\n content: \"\\e071\";\n}\n.glyphicon-play:before {\n content: \"\\e072\";\n}\n.glyphicon-pause:before {\n content: \"\\e073\";\n}\n.glyphicon-stop:before {\n content: \"\\e074\";\n}\n.glyphicon-forward:before {\n content: \"\\e075\";\n}\n.glyphicon-fast-forward:before {\n content: \"\\e076\";\n}\n.glyphicon-step-forward:before {\n content: \"\\e077\";\n}\n.glyphicon-eject:before {\n content: \"\\e078\";\n}\n.glyphicon-chevron-left:before {\n content: \"\\e079\";\n}\n.glyphicon-chevron-right:before {\n content: \"\\e080\";\n}\n.glyphicon-plus-sign:before {\n content: \"\\e081\";\n}\n.glyphicon-minus-sign:before {\n content: \"\\e082\";\n}\n.glyphicon-remove-sign:before {\n content: \"\\e083\";\n}\n.glyphicon-ok-sign:before {\n content: \"\\e084\";\n}\n.glyphicon-question-sign:before {\n content: \"\\e085\";\n}\n.glyphicon-info-sign:before {\n content: \"\\e086\";\n}\n.glyphicon-screenshot:before {\n content: \"\\e087\";\n}\n.glyphicon-remove-circle:before {\n content: \"\\e088\";\n}\n.glyphicon-ok-circle:before {\n content: \"\\e089\";\n}\n.glyphicon-ban-circle:before {\n content: \"\\e090\";\n}\n.glyphicon-arrow-left:before {\n content: \"\\e091\";\n}\n.glyphicon-arrow-right:before {\n content: \"\\e092\";\n}\n.glyphicon-arrow-up:before {\n content: \"\\e093\";\n}\n.glyphicon-arrow-down:before {\n content: \"\\e094\";\n}\n.glyphicon-share-alt:before {\n content: \"\\e095\";\n}\n.glyphicon-resize-full:before {\n content: \"\\e096\";\n}\n.glyphicon-resize-small:before {\n content: \"\\e097\";\n}\n.glyphicon-exclamation-sign:before {\n content: \"\\e101\";\n}\n.glyphicon-gift:before {\n content: \"\\e102\";\n}\n.glyphicon-leaf:before {\n content: \"\\e103\";\n}\n.glyphicon-fire:before {\n content: \"\\e104\";\n}\n.glyphicon-eye-open:before {\n content: \"\\e105\";\n}\n.glyphicon-eye-close:before {\n content: \"\\e106\";\n}\n.glyphicon-warning-sign:before {\n content: \"\\e107\";\n}\n.glyphicon-plane:before {\n content: \"\\e108\";\n}\n.glyphicon-calendar:before {\n content: \"\\e109\";\n}\n.glyphicon-random:before {\n content: \"\\e110\";\n}\n.glyphicon-comment:before {\n content: \"\\e111\";\n}\n.glyphicon-magnet:before {\n content: \"\\e112\";\n}\n.glyphicon-chevron-up:before {\n content: \"\\e113\";\n}\n.glyphicon-chevron-down:before {\n content: \"\\e114\";\n}\n.glyphicon-retweet:before {\n content: \"\\e115\";\n}\n.glyphicon-shopping-cart:before {\n content: \"\\e116\";\n}\n.glyphicon-folder-close:before {\n content: \"\\e117\";\n}\n.glyphicon-folder-open:before {\n content: \"\\e118\";\n}\n.glyphicon-resize-vertical:before {\n content: \"\\e119\";\n}\n.glyphicon-resize-horizontal:before {\n content: \"\\e120\";\n}\n.glyphicon-hdd:before {\n content: \"\\e121\";\n}\n.glyphicon-bullhorn:before {\n content: \"\\e122\";\n}\n.glyphicon-bell:before {\n content: \"\\e123\";\n}\n.glyphicon-certificate:before {\n content: \"\\e124\";\n}\n.glyphicon-thumbs-up:before {\n content: \"\\e125\";\n}\n.glyphicon-thumbs-down:before {\n content: \"\\e126\";\n}\n.glyphicon-hand-right:before {\n content: \"\\e127\";\n}\n.glyphicon-hand-left:before {\n content: \"\\e128\";\n}\n.glyphicon-hand-up:before {\n content: \"\\e129\";\n}\n.glyphicon-hand-down:before {\n content: \"\\e130\";\n}\n.glyphicon-circle-arrow-right:before {\n content: \"\\e131\";\n}\n.glyphicon-circle-arrow-left:before {\n content: \"\\e132\";\n}\n.glyphicon-circle-arrow-up:before {\n content: \"\\e133\";\n}\n.glyphicon-circle-arrow-down:before {\n content: \"\\e134\";\n}\n.glyphicon-globe:before {\n content: \"\\e135\";\n}\n.glyphicon-wrench:before {\n content: \"\\e136\";\n}\n.glyphicon-tasks:before {\n content: \"\\e137\";\n}\n.glyphicon-filter:before {\n content: \"\\e138\";\n}\n.glyphicon-briefcase:before {\n content: \"\\e139\";\n}\n.glyphicon-fullscreen:before {\n content: \"\\e140\";\n}\n.glyphicon-dashboard:before {\n content: \"\\e141\";\n}\n.glyphicon-paperclip:before {\n content: \"\\e142\";\n}\n.glyphicon-heart-empty:before {\n content: \"\\e143\";\n}\n.glyphicon-link:before {\n content: \"\\e144\";\n}\n.glyphicon-phone:before {\n content: \"\\e145\";\n}\n.glyphicon-pushpin:before {\n content: \"\\e146\";\n}\n.glyphicon-usd:before {\n content: \"\\e148\";\n}\n.glyphicon-gbp:before {\n content: \"\\e149\";\n}\n.glyphicon-sort:before {\n content: \"\\e150\";\n}\n.glyphicon-sort-by-alphabet:before {\n content: \"\\e151\";\n}\n.glyphicon-sort-by-alphabet-alt:before {\n content: \"\\e152\";\n}\n.glyphicon-sort-by-order:before {\n content: \"\\e153\";\n}\n.glyphicon-sort-by-order-alt:before {\n content: \"\\e154\";\n}\n.glyphicon-sort-by-attributes:before {\n content: \"\\e155\";\n}\n.glyphicon-sort-by-attributes-alt:before {\n content: \"\\e156\";\n}\n.glyphicon-unchecked:before {\n content: \"\\e157\";\n}\n.glyphicon-expand:before {\n content: \"\\e158\";\n}\n.glyphicon-collapse-down:before {\n content: \"\\e159\";\n}\n.glyphicon-collapse-up:before {\n content: \"\\e160\";\n}\n.glyphicon-log-in:before {\n content: \"\\e161\";\n}\n.glyphicon-flash:before {\n content: \"\\e162\";\n}\n.glyphicon-log-out:before {\n content: \"\\e163\";\n}\n.glyphicon-new-window:before {\n content: \"\\e164\";\n}\n.glyphicon-record:before {\n content: \"\\e165\";\n}\n.glyphicon-save:before {\n content: \"\\e166\";\n}\n.glyphicon-open:before {\n content: \"\\e167\";\n}\n.glyphicon-saved:before {\n content: \"\\e168\";\n}\n.glyphicon-import:before {\n content: \"\\e169\";\n}\n.glyphicon-export:before {\n content: \"\\e170\";\n}\n.glyphicon-send:before {\n content: \"\\e171\";\n}\n.glyphicon-floppy-disk:before {\n content: \"\\e172\";\n}\n.glyphicon-floppy-saved:before {\n content: \"\\e173\";\n}\n.glyphicon-floppy-remove:before {\n content: \"\\e174\";\n}\n.glyphicon-floppy-save:before {\n content: \"\\e175\";\n}\n.glyphicon-floppy-open:before {\n content: \"\\e176\";\n}\n.glyphicon-credit-card:before {\n content: \"\\e177\";\n}\n.glyphicon-transfer:before {\n content: \"\\e178\";\n}\n.glyphicon-cutlery:before {\n content: \"\\e179\";\n}\n.glyphicon-header:before {\n content: \"\\e180\";\n}\n.glyphicon-compressed:before {\n content: \"\\e181\";\n}\n.glyphicon-earphone:before {\n content: \"\\e182\";\n}\n.glyphicon-phone-alt:before {\n content: \"\\e183\";\n}\n.glyphicon-tower:before {\n content: \"\\e184\";\n}\n.glyphicon-stats:before {\n content: \"\\e185\";\n}\n.glyphicon-sd-video:before {\n content: \"\\e186\";\n}\n.glyphicon-hd-video:before {\n content: \"\\e187\";\n}\n.glyphicon-subtitles:before {\n content: \"\\e188\";\n}\n.glyphicon-sound-stereo:before {\n content: \"\\e189\";\n}\n.glyphicon-sound-dolby:before {\n content: \"\\e190\";\n}\n.glyphicon-sound-5-1:before {\n content: \"\\e191\";\n}\n.glyphicon-sound-6-1:before {\n content: \"\\e192\";\n}\n.glyphicon-sound-7-1:before {\n content: \"\\e193\";\n}\n.glyphicon-copyright-mark:before {\n content: \"\\e194\";\n}\n.glyphicon-registration-mark:before {\n content: \"\\e195\";\n}\n.glyphicon-cloud-download:before {\n content: \"\\e197\";\n}\n.glyphicon-cloud-upload:before {\n content: \"\\e198\";\n}\n.glyphicon-tree-conifer:before {\n content: \"\\e199\";\n}\n.glyphicon-tree-deciduous:before {\n content: \"\\e200\";\n}\n.glyphicon-cd:before {\n content: \"\\e201\";\n}\n.glyphicon-save-file:before {\n content: \"\\e202\";\n}\n.glyphicon-open-file:before {\n content: \"\\e203\";\n}\n.glyphicon-level-up:before {\n content: \"\\e204\";\n}\n.glyphicon-copy:before {\n content: \"\\e205\";\n}\n.glyphicon-paste:before {\n content: \"\\e206\";\n}\n.glyphicon-alert:before {\n content: \"\\e209\";\n}\n.glyphicon-equalizer:before {\n content: \"\\e210\";\n}\n.glyphicon-king:before {\n content: \"\\e211\";\n}\n.glyphicon-queen:before {\n content: \"\\e212\";\n}\n.glyphicon-pawn:before {\n content: \"\\e213\";\n}\n.glyphicon-bishop:before {\n content: \"\\e214\";\n}\n.glyphicon-knight:before {\n content: \"\\e215\";\n}\n.glyphicon-baby-formula:before {\n content: \"\\e216\";\n}\n.glyphicon-tent:before {\n content: \"\\26fa\";\n}\n.glyphicon-blackboard:before {\n content: \"\\e218\";\n}\n.glyphicon-bed:before {\n content: \"\\e219\";\n}\n.glyphicon-apple:before {\n content: \"\\f8ff\";\n}\n.glyphicon-erase:before {\n content: \"\\e221\";\n}\n.glyphicon-hourglass:before {\n content: \"\\231b\";\n}\n.glyphicon-lamp:before {\n content: \"\\e223\";\n}\n.glyphicon-duplicate:before {\n content: \"\\e224\";\n}\n.glyphicon-piggy-bank:before {\n content: \"\\e225\";\n}\n.glyphicon-scissors:before {\n content: \"\\e226\";\n}\n.glyphicon-bitcoin:before {\n content: \"\\e227\";\n}\n.glyphicon-btc:before {\n content: \"\\e227\";\n}\n.glyphicon-xbt:before {\n content: \"\\e227\";\n}\n.glyphicon-yen:before {\n content: \"\\00a5\";\n}\n.glyphicon-jpy:before {\n content: \"\\00a5\";\n}\n.glyphicon-ruble:before {\n content: \"\\20bd\";\n}\n.glyphicon-rub:before {\n content: \"\\20bd\";\n}\n.glyphicon-scale:before {\n content: \"\\e230\";\n}\n.glyphicon-ice-lolly:before {\n content: \"\\e231\";\n}\n.glyphicon-ice-lolly-tasted:before {\n content: \"\\e232\";\n}\n.glyphicon-education:before {\n content: \"\\e233\";\n}\n.glyphicon-option-horizontal:before {\n content: \"\\e234\";\n}\n.glyphicon-option-vertical:before {\n content: \"\\e235\";\n}\n.glyphicon-menu-hamburger:before {\n content: \"\\e236\";\n}\n.glyphicon-modal-window:before {\n content: \"\\e237\";\n}\n.glyphicon-oil:before {\n content: \"\\e238\";\n}\n.glyphicon-grain:before {\n content: \"\\e239\";\n}\n.glyphicon-sunglasses:before {\n content: \"\\e240\";\n}\n.glyphicon-text-size:before {\n content: \"\\e241\";\n}\n.glyphicon-text-color:before {\n content: \"\\e242\";\n}\n.glyphicon-text-background:before {\n content: \"\\e243\";\n}\n.glyphicon-object-align-top:before {\n content: \"\\e244\";\n}\n.glyphicon-object-align-bottom:before {\n content: \"\\e245\";\n}\n.glyphicon-object-align-horizontal:before {\n content: \"\\e246\";\n}\n.glyphicon-object-align-left:before {\n content: \"\\e247\";\n}\n.glyphicon-object-align-vertical:before {\n content: \"\\e248\";\n}\n.glyphicon-object-align-right:before {\n content: \"\\e249\";\n}\n.glyphicon-triangle-right:before {\n content: \"\\e250\";\n}\n.glyphicon-triangle-left:before {\n content: \"\\e251\";\n}\n.glyphicon-triangle-bottom:before {\n content: \"\\e252\";\n}\n.glyphicon-triangle-top:before {\n content: \"\\e253\";\n}\n.glyphicon-console:before {\n content: \"\\e254\";\n}\n.glyphicon-superscript:before {\n content: \"\\e255\";\n}\n.glyphicon-subscript:before {\n content: \"\\e256\";\n}\n.glyphicon-menu-left:before {\n content: \"\\e257\";\n}\n.glyphicon-menu-right:before {\n content: \"\\e258\";\n}\n.glyphicon-menu-down:before {\n content: \"\\e259\";\n}\n.glyphicon-menu-up:before {\n content: \"\\e260\";\n}\n* {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\n*:before,\n*:after {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\nhtml {\n font-size: 10px;\n\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\nbody {\n font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n font-size: 14px;\n line-height: 1.42857143;\n color: #333;\n background-color: #fff;\n}\ninput,\nbutton,\nselect,\ntextarea {\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\na {\n color: #337ab7;\n text-decoration: none;\n}\na:hover,\na:focus {\n color: #23527c;\n text-decoration: underline;\n}\na:focus {\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\nfigure {\n margin: 0;\n}\nimg {\n vertical-align: middle;\n}\n.img-responsive,\n.thumbnail > img,\n.thumbnail a > img,\n.carousel-inner > .item > img,\n.carousel-inner > .item > a > img {\n display: block;\n max-width: 100%;\n height: auto;\n}\n.img-rounded {\n border-radius: 6px;\n}\n.img-thumbnail {\n display: inline-block;\n max-width: 100%;\n height: auto;\n padding: 4px;\n line-height: 1.42857143;\n background-color: #fff;\n border: 1px solid #ddd;\n border-radius: 4px;\n -webkit-transition: all .2s ease-in-out;\n -o-transition: all .2s ease-in-out;\n transition: all .2s ease-in-out;\n}\n.img-circle {\n border-radius: 50%;\n}\nhr {\n margin-top: 20px;\n margin-bottom: 20px;\n border: 0;\n border-top: 1px solid #eee;\n}\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n border: 0;\n}\n.sr-only-focusable:active,\n.sr-only-focusable:focus {\n position: static;\n width: auto;\n height: auto;\n margin: 0;\n overflow: visible;\n clip: auto;\n}\n[role=\"button\"] {\n cursor: pointer;\n}\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\n.h1,\n.h2,\n.h3,\n.h4,\n.h5,\n.h6 {\n font-family: inherit;\n font-weight: 500;\n line-height: 1.1;\n color: inherit;\n}\nh1 small,\nh2 small,\nh3 small,\nh4 small,\nh5 small,\nh6 small,\n.h1 small,\n.h2 small,\n.h3 small,\n.h4 small,\n.h5 small,\n.h6 small,\nh1 .small,\nh2 .small,\nh3 .small,\nh4 .small,\nh5 .small,\nh6 .small,\n.h1 .small,\n.h2 .small,\n.h3 .small,\n.h4 .small,\n.h5 .small,\n.h6 .small {\n font-weight: normal;\n line-height: 1;\n color: #777;\n}\nh1,\n.h1,\nh2,\n.h2,\nh3,\n.h3 {\n margin-top: 20px;\n margin-bottom: 10px;\n}\nh1 small,\n.h1 small,\nh2 small,\n.h2 small,\nh3 small,\n.h3 small,\nh1 .small,\n.h1 .small,\nh2 .small,\n.h2 .small,\nh3 .small,\n.h3 .small {\n font-size: 65%;\n}\nh4,\n.h4,\nh5,\n.h5,\nh6,\n.h6 {\n margin-top: 10px;\n margin-bottom: 10px;\n}\nh4 small,\n.h4 small,\nh5 small,\n.h5 small,\nh6 small,\n.h6 small,\nh4 .small,\n.h4 .small,\nh5 .small,\n.h5 .small,\nh6 .small,\n.h6 .small {\n font-size: 75%;\n}\nh1,\n.h1 {\n font-size: 36px;\n}\nh2,\n.h2 {\n font-size: 30px;\n}\nh3,\n.h3 {\n font-size: 24px;\n}\nh4,\n.h4 {\n font-size: 18px;\n}\nh5,\n.h5 {\n font-size: 14px;\n}\nh6,\n.h6 {\n font-size: 12px;\n}\np {\n margin: 0 0 10px;\n}\n.lead {\n margin-bottom: 20px;\n font-size: 16px;\n font-weight: 300;\n line-height: 1.4;\n}\n@media (min-width: 768px) {\n .lead {\n font-size: 21px;\n }\n}\nsmall,\n.small {\n font-size: 85%;\n}\nmark,\n.mark {\n padding: .2em;\n background-color: #fcf8e3;\n}\n.text-left {\n text-align: left;\n}\n.text-right {\n text-align: right;\n}\n.text-center {\n text-align: center;\n}\n.text-justify {\n text-align: justify;\n}\n.text-nowrap {\n white-space: nowrap;\n}\n.text-lowercase {\n text-transform: lowercase;\n}\n.text-uppercase {\n text-transform: uppercase;\n}\n.text-capitalize {\n text-transform: capitalize;\n}\n.text-muted {\n color: #777;\n}\n.text-primary {\n color: #337ab7;\n}\na.text-primary:hover,\na.text-primary:focus {\n color: #286090;\n}\n.text-success {\n color: #3c763d;\n}\na.text-success:hover,\na.text-success:focus {\n color: #2b542c;\n}\n.text-info {\n color: #31708f;\n}\na.text-info:hover,\na.text-info:focus {\n color: #245269;\n}\n.text-warning {\n color: #8a6d3b;\n}\na.text-warning:hover,\na.text-warning:focus {\n color: #66512c;\n}\n.text-danger {\n color: #a94442;\n}\na.text-danger:hover,\na.text-danger:focus {\n color: #843534;\n}\n.bg-primary {\n color: #fff;\n background-color: #337ab7;\n}\na.bg-primary:hover,\na.bg-primary:focus {\n background-color: #286090;\n}\n.bg-success {\n background-color: #dff0d8;\n}\na.bg-success:hover,\na.bg-success:focus {\n background-color: #c1e2b3;\n}\n.bg-info {\n background-color: #d9edf7;\n}\na.bg-info:hover,\na.bg-info:focus {\n background-color: #afd9ee;\n}\n.bg-warning {\n background-color: #fcf8e3;\n}\na.bg-warning:hover,\na.bg-warning:focus {\n background-color: #f7ecb5;\n}\n.bg-danger {\n background-color: #f2dede;\n}\na.bg-danger:hover,\na.bg-danger:focus {\n background-color: #e4b9b9;\n}\n.page-header {\n padding-bottom: 9px;\n margin: 40px 0 20px;\n border-bottom: 1px solid #eee;\n}\nul,\nol {\n margin-top: 0;\n margin-bottom: 10px;\n}\nul ul,\nol ul,\nul ol,\nol ol {\n margin-bottom: 0;\n}\n.list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n.list-inline {\n padding-left: 0;\n margin-left: -5px;\n list-style: none;\n}\n.list-inline > li {\n display: inline-block;\n padding-right: 5px;\n padding-left: 5px;\n}\ndl {\n margin-top: 0;\n margin-bottom: 20px;\n}\ndt,\ndd {\n line-height: 1.42857143;\n}\ndt {\n font-weight: bold;\n}\ndd {\n margin-left: 0;\n}\n@media (min-width: 768px) {\n .dl-horizontal dt {\n float: left;\n width: 160px;\n overflow: hidden;\n clear: left;\n text-align: right;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n .dl-horizontal dd {\n margin-left: 180px;\n }\n}\nabbr[title],\nabbr[data-original-title] {\n cursor: help;\n border-bottom: 1px dotted #777;\n}\n.initialism {\n font-size: 90%;\n text-transform: uppercase;\n}\nblockquote {\n padding: 10px 20px;\n margin: 0 0 20px;\n font-size: 17.5px;\n border-left: 5px solid #eee;\n}\nblockquote p:last-child,\nblockquote ul:last-child,\nblockquote ol:last-child {\n margin-bottom: 0;\n}\nblockquote footer,\nblockquote small,\nblockquote .small {\n display: block;\n font-size: 80%;\n line-height: 1.42857143;\n color: #777;\n}\nblockquote footer:before,\nblockquote small:before,\nblockquote .small:before {\n content: '\\2014 \\00A0';\n}\n.blockquote-reverse,\nblockquote.pull-right {\n padding-right: 15px;\n padding-left: 0;\n text-align: right;\n border-right: 5px solid #eee;\n border-left: 0;\n}\n.blockquote-reverse footer:before,\nblockquote.pull-right footer:before,\n.blockquote-reverse small:before,\nblockquote.pull-right small:before,\n.blockquote-reverse .small:before,\nblockquote.pull-right .small:before {\n content: '';\n}\n.blockquote-reverse footer:after,\nblockquote.pull-right footer:after,\n.blockquote-reverse small:after,\nblockquote.pull-right small:after,\n.blockquote-reverse .small:after,\nblockquote.pull-right .small:after {\n content: '\\00A0 \\2014';\n}\naddress {\n margin-bottom: 20px;\n font-style: normal;\n line-height: 1.42857143;\n}\ncode,\nkbd,\npre,\nsamp {\n font-family: Menlo, Monaco, Consolas, \"Courier New\", monospace;\n}\ncode {\n padding: 2px 4px;\n font-size: 90%;\n color: #c7254e;\n background-color: #f9f2f4;\n border-radius: 4px;\n}\nkbd {\n padding: 2px 4px;\n font-size: 90%;\n color: #fff;\n background-color: #333;\n border-radius: 3px;\n -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25);\n box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25);\n}\nkbd kbd {\n padding: 0;\n font-size: 100%;\n font-weight: bold;\n -webkit-box-shadow: none;\n box-shadow: none;\n}\npre {\n display: block;\n padding: 9.5px;\n margin: 0 0 10px;\n font-size: 13px;\n line-height: 1.42857143;\n color: #333;\n word-break: break-all;\n word-wrap: break-word;\n background-color: #f5f5f5;\n border: 1px solid #ccc;\n border-radius: 4px;\n}\npre code {\n padding: 0;\n font-size: inherit;\n color: inherit;\n white-space: pre-wrap;\n background-color: transparent;\n border-radius: 0;\n}\n.pre-scrollable {\n max-height: 340px;\n overflow-y: scroll;\n}\n.container {\n padding-right: 15px;\n padding-left: 15px;\n margin-right: auto;\n margin-left: auto;\n}\n@media (min-width: 768px) {\n .container {\n width: 750px;\n }\n}\n@media (min-width: 992px) {\n .container {\n width: 970px;\n }\n}\n@media (min-width: 1200px) {\n .container {\n width: 1170px;\n }\n}\n.container-fluid {\n padding-right: 15px;\n padding-left: 15px;\n margin-right: auto;\n margin-left: auto;\n}\n.row {\n margin-right: -15px;\n margin-left: -15px;\n}\n.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 {\n position: relative;\n min-height: 1px;\n padding-right: 15px;\n padding-left: 15px;\n}\n.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 {\n float: left;\n}\n.col-xs-12 {\n width: 100%;\n}\n.col-xs-11 {\n width: 91.66666667%;\n}\n.col-xs-10 {\n width: 83.33333333%;\n}\n.col-xs-9 {\n width: 75%;\n}\n.col-xs-8 {\n width: 66.66666667%;\n}\n.col-xs-7 {\n width: 58.33333333%;\n}\n.col-xs-6 {\n width: 50%;\n}\n.col-xs-5 {\n width: 41.66666667%;\n}\n.col-xs-4 {\n width: 33.33333333%;\n}\n.col-xs-3 {\n width: 25%;\n}\n.col-xs-2 {\n width: 16.66666667%;\n}\n.col-xs-1 {\n width: 8.33333333%;\n}\n.col-xs-pull-12 {\n right: 100%;\n}\n.col-xs-pull-11 {\n right: 91.66666667%;\n}\n.col-xs-pull-10 {\n right: 83.33333333%;\n}\n.col-xs-pull-9 {\n right: 75%;\n}\n.col-xs-pull-8 {\n right: 66.66666667%;\n}\n.col-xs-pull-7 {\n right: 58.33333333%;\n}\n.col-xs-pull-6 {\n right: 50%;\n}\n.col-xs-pull-5 {\n right: 41.66666667%;\n}\n.col-xs-pull-4 {\n right: 33.33333333%;\n}\n.col-xs-pull-3 {\n right: 25%;\n}\n.col-xs-pull-2 {\n right: 16.66666667%;\n}\n.col-xs-pull-1 {\n right: 8.33333333%;\n}\n.col-xs-pull-0 {\n right: auto;\n}\n.col-xs-push-12 {\n left: 100%;\n}\n.col-xs-push-11 {\n left: 91.66666667%;\n}\n.col-xs-push-10 {\n left: 83.33333333%;\n}\n.col-xs-push-9 {\n left: 75%;\n}\n.col-xs-push-8 {\n left: 66.66666667%;\n}\n.col-xs-push-7 {\n left: 58.33333333%;\n}\n.col-xs-push-6 {\n left: 50%;\n}\n.col-xs-push-5 {\n left: 41.66666667%;\n}\n.col-xs-push-4 {\n left: 33.33333333%;\n}\n.col-xs-push-3 {\n left: 25%;\n}\n.col-xs-push-2 {\n left: 16.66666667%;\n}\n.col-xs-push-1 {\n left: 8.33333333%;\n}\n.col-xs-push-0 {\n left: auto;\n}\n.col-xs-offset-12 {\n margin-left: 100%;\n}\n.col-xs-offset-11 {\n margin-left: 91.66666667%;\n}\n.col-xs-offset-10 {\n margin-left: 83.33333333%;\n}\n.col-xs-offset-9 {\n margin-left: 75%;\n}\n.col-xs-offset-8 {\n margin-left: 66.66666667%;\n}\n.col-xs-offset-7 {\n margin-left: 58.33333333%;\n}\n.col-xs-offset-6 {\n margin-left: 50%;\n}\n.col-xs-offset-5 {\n margin-left: 41.66666667%;\n}\n.col-xs-offset-4 {\n margin-left: 33.33333333%;\n}\n.col-xs-offset-3 {\n margin-left: 25%;\n}\n.col-xs-offset-2 {\n margin-left: 16.66666667%;\n}\n.col-xs-offset-1 {\n margin-left: 8.33333333%;\n}\n.col-xs-offset-0 {\n margin-left: 0;\n}\n@media (min-width: 768px) {\n .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 {\n float: left;\n }\n .col-sm-12 {\n width: 100%;\n }\n .col-sm-11 {\n width: 91.66666667%;\n }\n .col-sm-10 {\n width: 83.33333333%;\n }\n .col-sm-9 {\n width: 75%;\n }\n .col-sm-8 {\n width: 66.66666667%;\n }\n .col-sm-7 {\n width: 58.33333333%;\n }\n .col-sm-6 {\n width: 50%;\n }\n .col-sm-5 {\n width: 41.66666667%;\n }\n .col-sm-4 {\n width: 33.33333333%;\n }\n .col-sm-3 {\n width: 25%;\n }\n .col-sm-2 {\n width: 16.66666667%;\n }\n .col-sm-1 {\n width: 8.33333333%;\n }\n .col-sm-pull-12 {\n right: 100%;\n }\n .col-sm-pull-11 {\n right: 91.66666667%;\n }\n .col-sm-pull-10 {\n right: 83.33333333%;\n }\n .col-sm-pull-9 {\n right: 75%;\n }\n .col-sm-pull-8 {\n right: 66.66666667%;\n }\n .col-sm-pull-7 {\n right: 58.33333333%;\n }\n .col-sm-pull-6 {\n right: 50%;\n }\n .col-sm-pull-5 {\n right: 41.66666667%;\n }\n .col-sm-pull-4 {\n right: 33.33333333%;\n }\n .col-sm-pull-3 {\n right: 25%;\n }\n .col-sm-pull-2 {\n right: 16.66666667%;\n }\n .col-sm-pull-1 {\n right: 8.33333333%;\n }\n .col-sm-pull-0 {\n right: auto;\n }\n .col-sm-push-12 {\n left: 100%;\n }\n .col-sm-push-11 {\n left: 91.66666667%;\n }\n .col-sm-push-10 {\n left: 83.33333333%;\n }\n .col-sm-push-9 {\n left: 75%;\n }\n .col-sm-push-8 {\n left: 66.66666667%;\n }\n .col-sm-push-7 {\n left: 58.33333333%;\n }\n .col-sm-push-6 {\n left: 50%;\n }\n .col-sm-push-5 {\n left: 41.66666667%;\n }\n .col-sm-push-4 {\n left: 33.33333333%;\n }\n .col-sm-push-3 {\n left: 25%;\n }\n .col-sm-push-2 {\n left: 16.66666667%;\n }\n .col-sm-push-1 {\n left: 8.33333333%;\n }\n .col-sm-push-0 {\n left: auto;\n }\n .col-sm-offset-12 {\n margin-left: 100%;\n }\n .col-sm-offset-11 {\n margin-left: 91.66666667%;\n }\n .col-sm-offset-10 {\n margin-left: 83.33333333%;\n }\n .col-sm-offset-9 {\n margin-left: 75%;\n }\n .col-sm-offset-8 {\n margin-left: 66.66666667%;\n }\n .col-sm-offset-7 {\n margin-left: 58.33333333%;\n }\n .col-sm-offset-6 {\n margin-left: 50%;\n }\n .col-sm-offset-5 {\n margin-left: 41.66666667%;\n }\n .col-sm-offset-4 {\n margin-left: 33.33333333%;\n }\n .col-sm-offset-3 {\n margin-left: 25%;\n }\n .col-sm-offset-2 {\n margin-left: 16.66666667%;\n }\n .col-sm-offset-1 {\n margin-left: 8.33333333%;\n }\n .col-sm-offset-0 {\n margin-left: 0;\n }\n}\n@media (min-width: 992px) {\n .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 {\n float: left;\n }\n .col-md-12 {\n width: 100%;\n }\n .col-md-11 {\n width: 91.66666667%;\n }\n .col-md-10 {\n width: 83.33333333%;\n }\n .col-md-9 {\n width: 75%;\n }\n .col-md-8 {\n width: 66.66666667%;\n }\n .col-md-7 {\n width: 58.33333333%;\n }\n .col-md-6 {\n width: 50%;\n }\n .col-md-5 {\n width: 41.66666667%;\n }\n .col-md-4 {\n width: 33.33333333%;\n }\n .col-md-3 {\n width: 25%;\n }\n .col-md-2 {\n width: 16.66666667%;\n }\n .col-md-1 {\n width: 8.33333333%;\n }\n .col-md-pull-12 {\n right: 100%;\n }\n .col-md-pull-11 {\n right: 91.66666667%;\n }\n .col-md-pull-10 {\n right: 83.33333333%;\n }\n .col-md-pull-9 {\n right: 75%;\n }\n .col-md-pull-8 {\n right: 66.66666667%;\n }\n .col-md-pull-7 {\n right: 58.33333333%;\n }\n .col-md-pull-6 {\n right: 50%;\n }\n .col-md-pull-5 {\n right: 41.66666667%;\n }\n .col-md-pull-4 {\n right: 33.33333333%;\n }\n .col-md-pull-3 {\n right: 25%;\n }\n .col-md-pull-2 {\n right: 16.66666667%;\n }\n .col-md-pull-1 {\n right: 8.33333333%;\n }\n .col-md-pull-0 {\n right: auto;\n }\n .col-md-push-12 {\n left: 100%;\n }\n .col-md-push-11 {\n left: 91.66666667%;\n }\n .col-md-push-10 {\n left: 83.33333333%;\n }\n .col-md-push-9 {\n left: 75%;\n }\n .col-md-push-8 {\n left: 66.66666667%;\n }\n .col-md-push-7 {\n left: 58.33333333%;\n }\n .col-md-push-6 {\n left: 50%;\n }\n .col-md-push-5 {\n left: 41.66666667%;\n }\n .col-md-push-4 {\n left: 33.33333333%;\n }\n .col-md-push-3 {\n left: 25%;\n }\n .col-md-push-2 {\n left: 16.66666667%;\n }\n .col-md-push-1 {\n left: 8.33333333%;\n }\n .col-md-push-0 {\n left: auto;\n }\n .col-md-offset-12 {\n margin-left: 100%;\n }\n .col-md-offset-11 {\n margin-left: 91.66666667%;\n }\n .col-md-offset-10 {\n margin-left: 83.33333333%;\n }\n .col-md-offset-9 {\n margin-left: 75%;\n }\n .col-md-offset-8 {\n margin-left: 66.66666667%;\n }\n .col-md-offset-7 {\n margin-left: 58.33333333%;\n }\n .col-md-offset-6 {\n margin-left: 50%;\n }\n .col-md-offset-5 {\n margin-left: 41.66666667%;\n }\n .col-md-offset-4 {\n margin-left: 33.33333333%;\n }\n .col-md-offset-3 {\n margin-left: 25%;\n }\n .col-md-offset-2 {\n margin-left: 16.66666667%;\n }\n .col-md-offset-1 {\n margin-left: 8.33333333%;\n }\n .col-md-offset-0 {\n margin-left: 0;\n }\n}\n@media (min-width: 1200px) {\n .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 {\n float: left;\n }\n .col-lg-12 {\n width: 100%;\n }\n .col-lg-11 {\n width: 91.66666667%;\n }\n .col-lg-10 {\n width: 83.33333333%;\n }\n .col-lg-9 {\n width: 75%;\n }\n .col-lg-8 {\n width: 66.66666667%;\n }\n .col-lg-7 {\n width: 58.33333333%;\n }\n .col-lg-6 {\n width: 50%;\n }\n .col-lg-5 {\n width: 41.66666667%;\n }\n .col-lg-4 {\n width: 33.33333333%;\n }\n .col-lg-3 {\n width: 25%;\n }\n .col-lg-2 {\n width: 16.66666667%;\n }\n .col-lg-1 {\n width: 8.33333333%;\n }\n .col-lg-pull-12 {\n right: 100%;\n }\n .col-lg-pull-11 {\n right: 91.66666667%;\n }\n .col-lg-pull-10 {\n right: 83.33333333%;\n }\n .col-lg-pull-9 {\n right: 75%;\n }\n .col-lg-pull-8 {\n right: 66.66666667%;\n }\n .col-lg-pull-7 {\n right: 58.33333333%;\n }\n .col-lg-pull-6 {\n right: 50%;\n }\n .col-lg-pull-5 {\n right: 41.66666667%;\n }\n .col-lg-pull-4 {\n right: 33.33333333%;\n }\n .col-lg-pull-3 {\n right: 25%;\n }\n .col-lg-pull-2 {\n right: 16.66666667%;\n }\n .col-lg-pull-1 {\n right: 8.33333333%;\n }\n .col-lg-pull-0 {\n right: auto;\n }\n .col-lg-push-12 {\n left: 100%;\n }\n .col-lg-push-11 {\n left: 91.66666667%;\n }\n .col-lg-push-10 {\n left: 83.33333333%;\n }\n .col-lg-push-9 {\n left: 75%;\n }\n .col-lg-push-8 {\n left: 66.66666667%;\n }\n .col-lg-push-7 {\n left: 58.33333333%;\n }\n .col-lg-push-6 {\n left: 50%;\n }\n .col-lg-push-5 {\n left: 41.66666667%;\n }\n .col-lg-push-4 {\n left: 33.33333333%;\n }\n .col-lg-push-3 {\n left: 25%;\n }\n .col-lg-push-2 {\n left: 16.66666667%;\n }\n .col-lg-push-1 {\n left: 8.33333333%;\n }\n .col-lg-push-0 {\n left: auto;\n }\n .col-lg-offset-12 {\n margin-left: 100%;\n }\n .col-lg-offset-11 {\n margin-left: 91.66666667%;\n }\n .col-lg-offset-10 {\n margin-left: 83.33333333%;\n }\n .col-lg-offset-9 {\n margin-left: 75%;\n }\n .col-lg-offset-8 {\n margin-left: 66.66666667%;\n }\n .col-lg-offset-7 {\n margin-left: 58.33333333%;\n }\n .col-lg-offset-6 {\n margin-left: 50%;\n }\n .col-lg-offset-5 {\n margin-left: 41.66666667%;\n }\n .col-lg-offset-4 {\n margin-left: 33.33333333%;\n }\n .col-lg-offset-3 {\n margin-left: 25%;\n }\n .col-lg-offset-2 {\n margin-left: 16.66666667%;\n }\n .col-lg-offset-1 {\n margin-left: 8.33333333%;\n }\n .col-lg-offset-0 {\n margin-left: 0;\n }\n}\ntable {\n background-color: transparent;\n}\ncaption {\n padding-top: 8px;\n padding-bottom: 8px;\n color: #777;\n text-align: left;\n}\nth {\n text-align: left;\n}\n.table {\n width: 100%;\n max-width: 100%;\n margin-bottom: 20px;\n}\n.table > thead > tr > th,\n.table > tbody > tr > th,\n.table > tfoot > tr > th,\n.table > thead > tr > td,\n.table > tbody > tr > td,\n.table > tfoot > tr > td {\n padding: 8px;\n line-height: 1.42857143;\n vertical-align: top;\n border-top: 1px solid #ddd;\n}\n.table > thead > tr > th {\n vertical-align: bottom;\n border-bottom: 2px solid #ddd;\n}\n.table > caption + thead > tr:first-child > th,\n.table > colgroup + thead > tr:first-child > th,\n.table > thead:first-child > tr:first-child > th,\n.table > caption + thead > tr:first-child > td,\n.table > colgroup + thead > tr:first-child > td,\n.table > thead:first-child > tr:first-child > td {\n border-top: 0;\n}\n.table > tbody + tbody {\n border-top: 2px solid #ddd;\n}\n.table .table {\n background-color: #fff;\n}\n.table-condensed > thead > tr > th,\n.table-condensed > tbody > tr > th,\n.table-condensed > tfoot > tr > th,\n.table-condensed > thead > tr > td,\n.table-condensed > tbody > tr > td,\n.table-condensed > tfoot > tr > td {\n padding: 5px;\n}\n.table-bordered {\n border: 1px solid #ddd;\n}\n.table-bordered > thead > tr > th,\n.table-bordered > tbody > tr > th,\n.table-bordered > tfoot > tr > th,\n.table-bordered > thead > tr > td,\n.table-bordered > tbody > tr > td,\n.table-bordered > tfoot > tr > td {\n border: 1px solid #ddd;\n}\n.table-bordered > thead > tr > th,\n.table-bordered > thead > tr > td {\n border-bottom-width: 2px;\n}\n.table-striped > tbody > tr:nth-of-type(odd) {\n background-color: #f9f9f9;\n}\n.table-hover > tbody > tr:hover {\n background-color: #f5f5f5;\n}\ntable col[class*=\"col-\"] {\n position: static;\n display: table-column;\n float: none;\n}\ntable td[class*=\"col-\"],\ntable th[class*=\"col-\"] {\n position: static;\n display: table-cell;\n float: none;\n}\n.table > thead > tr > td.active,\n.table > tbody > tr > td.active,\n.table > tfoot > tr > td.active,\n.table > thead > tr > th.active,\n.table > tbody > tr > th.active,\n.table > tfoot > tr > th.active,\n.table > thead > tr.active > td,\n.table > tbody > tr.active > td,\n.table > tfoot > tr.active > td,\n.table > thead > tr.active > th,\n.table > tbody > tr.active > th,\n.table > tfoot > tr.active > th {\n background-color: #f5f5f5;\n}\n.table-hover > tbody > tr > td.active:hover,\n.table-hover > tbody > tr > th.active:hover,\n.table-hover > tbody > tr.active:hover > td,\n.table-hover > tbody > tr:hover > .active,\n.table-hover > tbody > tr.active:hover > th {\n background-color: #e8e8e8;\n}\n.table > thead > tr > td.success,\n.table > tbody > tr > td.success,\n.table > tfoot > tr > td.success,\n.table > thead > tr > th.success,\n.table > tbody > tr > th.success,\n.table > tfoot > tr > th.success,\n.table > thead > tr.success > td,\n.table > tbody > tr.success > td,\n.table > tfoot > tr.success > td,\n.table > thead > tr.success > th,\n.table > tbody > tr.success > th,\n.table > tfoot > tr.success > th {\n background-color: #dff0d8;\n}\n.table-hover > tbody > tr > td.success:hover,\n.table-hover > tbody > tr > th.success:hover,\n.table-hover > tbody > tr.success:hover > td,\n.table-hover > tbody > tr:hover > .success,\n.table-hover > tbody > tr.success:hover > th {\n background-color: #d0e9c6;\n}\n.table > thead > tr > td.info,\n.table > tbody > tr > td.info,\n.table > tfoot > tr > td.info,\n.table > thead > tr > th.info,\n.table > tbody > tr > th.info,\n.table > tfoot > tr > th.info,\n.table > thead > tr.info > td,\n.table > tbody > tr.info > td,\n.table > tfoot > tr.info > td,\n.table > thead > tr.info > th,\n.table > tbody > tr.info > th,\n.table > tfoot > tr.info > th {\n background-color: #d9edf7;\n}\n.table-hover > tbody > tr > td.info:hover,\n.table-hover > tbody > tr > th.info:hover,\n.table-hover > tbody > tr.info:hover > td,\n.table-hover > tbody > tr:hover > .info,\n.table-hover > tbody > tr.info:hover > th {\n background-color: #c4e3f3;\n}\n.table > thead > tr > td.warning,\n.table > tbody > tr > td.warning,\n.table > tfoot > tr > td.warning,\n.table > thead > tr > th.warning,\n.table > tbody > tr > th.warning,\n.table > tfoot > tr > th.warning,\n.table > thead > tr.warning > td,\n.table > tbody > tr.warning > td,\n.table > tfoot > tr.warning > td,\n.table > thead > tr.warning > th,\n.table > tbody > tr.warning > th,\n.table > tfoot > tr.warning > th {\n background-color: #fcf8e3;\n}\n.table-hover > tbody > tr > td.warning:hover,\n.table-hover > tbody > tr > th.warning:hover,\n.table-hover > tbody > tr.warning:hover > td,\n.table-hover > tbody > tr:hover > .warning,\n.table-hover > tbody > tr.warning:hover > th {\n background-color: #faf2cc;\n}\n.table > thead > tr > td.danger,\n.table > tbody > tr > td.danger,\n.table > tfoot > tr > td.danger,\n.table > thead > tr > th.danger,\n.table > tbody > tr > th.danger,\n.table > tfoot > tr > th.danger,\n.table > thead > tr.danger > td,\n.table > tbody > tr.danger > td,\n.table > tfoot > tr.danger > td,\n.table > thead > tr.danger > th,\n.table > tbody > tr.danger > th,\n.table > tfoot > tr.danger > th {\n background-color: #f2dede;\n}\n.table-hover > tbody > tr > td.danger:hover,\n.table-hover > tbody > tr > th.danger:hover,\n.table-hover > tbody > tr.danger:hover > td,\n.table-hover > tbody > tr:hover > .danger,\n.table-hover > tbody > tr.danger:hover > th {\n background-color: #ebcccc;\n}\n.table-responsive {\n min-height: .01%;\n overflow-x: auto;\n}\n@media screen and (max-width: 767px) {\n .table-responsive {\n width: 100%;\n margin-bottom: 15px;\n overflow-y: hidden;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n border: 1px solid #ddd;\n }\n .table-responsive > .table {\n margin-bottom: 0;\n }\n .table-responsive > .table > thead > tr > th,\n .table-responsive > .table > tbody > tr > th,\n .table-responsive > .table > tfoot > tr > th,\n .table-responsive > .table > thead > tr > td,\n .table-responsive > .table > tbody > tr > td,\n .table-responsive > .table > tfoot > tr > td {\n white-space: nowrap;\n }\n .table-responsive > .table-bordered {\n border: 0;\n }\n .table-responsive > .table-bordered > thead > tr > th:first-child,\n .table-responsive > .table-bordered > tbody > tr > th:first-child,\n .table-responsive > .table-bordered > tfoot > tr > th:first-child,\n .table-responsive > .table-bordered > thead > tr > td:first-child,\n .table-responsive > .table-bordered > tbody > tr > td:first-child,\n .table-responsive > .table-bordered > tfoot > tr > td:first-child {\n border-left: 0;\n }\n .table-responsive > .table-bordered > thead > tr > th:last-child,\n .table-responsive > .table-bordered > tbody > tr > th:last-child,\n .table-responsive > .table-bordered > tfoot > tr > th:last-child,\n .table-responsive > .table-bordered > thead > tr > td:last-child,\n .table-responsive > .table-bordered > tbody > tr > td:last-child,\n .table-responsive > .table-bordered > tfoot > tr > td:last-child {\n border-right: 0;\n }\n .table-responsive > .table-bordered > tbody > tr:last-child > th,\n .table-responsive > .table-bordered > tfoot > tr:last-child > th,\n .table-responsive > .table-bordered > tbody > tr:last-child > td,\n .table-responsive > .table-bordered > tfoot > tr:last-child > td {\n border-bottom: 0;\n }\n}\nfieldset {\n min-width: 0;\n padding: 0;\n margin: 0;\n border: 0;\n}\nlegend {\n display: block;\n width: 100%;\n padding: 0;\n margin-bottom: 20px;\n font-size: 21px;\n line-height: inherit;\n color: #333;\n border: 0;\n border-bottom: 1px solid #e5e5e5;\n}\nlabel {\n display: inline-block;\n max-width: 100%;\n margin-bottom: 5px;\n font-weight: bold;\n}\ninput[type=\"search\"] {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n margin: 4px 0 0;\n margin-top: 1px \\9;\n line-height: normal;\n}\ninput[type=\"file\"] {\n display: block;\n}\ninput[type=\"range\"] {\n display: block;\n width: 100%;\n}\nselect[multiple],\nselect[size] {\n height: auto;\n}\ninput[type=\"file\"]:focus,\ninput[type=\"radio\"]:focus,\ninput[type=\"checkbox\"]:focus {\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\noutput {\n display: block;\n padding-top: 7px;\n font-size: 14px;\n line-height: 1.42857143;\n color: #555;\n}\n.form-control {\n display: block;\n width: 100%;\n height: 34px;\n padding: 6px 12px;\n font-size: 14px;\n line-height: 1.42857143;\n color: #555;\n background-color: #fff;\n background-image: none;\n border: 1px solid #ccc;\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);\n -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s;\n -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n}\n.form-control:focus {\n border-color: #66afe9;\n outline: 0;\n -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6);\n box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6);\n}\n.form-control::-moz-placeholder {\n color: #999;\n opacity: 1;\n}\n.form-control:-ms-input-placeholder {\n color: #999;\n}\n.form-control::-webkit-input-placeholder {\n color: #999;\n}\n.form-control::-ms-expand {\n background-color: transparent;\n border: 0;\n}\n.form-control[disabled],\n.form-control[readonly],\nfieldset[disabled] .form-control {\n background-color: #eee;\n opacity: 1;\n}\n.form-control[disabled],\nfieldset[disabled] .form-control {\n cursor: not-allowed;\n}\ntextarea.form-control {\n height: auto;\n}\ninput[type=\"search\"] {\n -webkit-appearance: none;\n}\n@media screen and (-webkit-min-device-pixel-ratio: 0) {\n input[type=\"date\"].form-control,\n input[type=\"time\"].form-control,\n input[type=\"datetime-local\"].form-control,\n input[type=\"month\"].form-control {\n line-height: 34px;\n }\n input[type=\"date\"].input-sm,\n input[type=\"time\"].input-sm,\n input[type=\"datetime-local\"].input-sm,\n input[type=\"month\"].input-sm,\n .input-group-sm input[type=\"date\"],\n .input-group-sm input[type=\"time\"],\n .input-group-sm input[type=\"datetime-local\"],\n .input-group-sm input[type=\"month\"] {\n line-height: 30px;\n }\n input[type=\"date\"].input-lg,\n input[type=\"time\"].input-lg,\n input[type=\"datetime-local\"].input-lg,\n input[type=\"month\"].input-lg,\n .input-group-lg input[type=\"date\"],\n .input-group-lg input[type=\"time\"],\n .input-group-lg input[type=\"datetime-local\"],\n .input-group-lg input[type=\"month\"] {\n line-height: 46px;\n }\n}\n.form-group {\n margin-bottom: 15px;\n}\n.radio,\n.checkbox {\n position: relative;\n display: block;\n margin-top: 10px;\n margin-bottom: 10px;\n}\n.radio label,\n.checkbox label {\n min-height: 20px;\n padding-left: 20px;\n margin-bottom: 0;\n font-weight: normal;\n cursor: pointer;\n}\n.radio input[type=\"radio\"],\n.radio-inline input[type=\"radio\"],\n.checkbox input[type=\"checkbox\"],\n.checkbox-inline input[type=\"checkbox\"] {\n position: absolute;\n margin-top: 4px \\9;\n margin-left: -20px;\n}\n.radio + .radio,\n.checkbox + .checkbox {\n margin-top: -5px;\n}\n.radio-inline,\n.checkbox-inline {\n position: relative;\n display: inline-block;\n padding-left: 20px;\n margin-bottom: 0;\n font-weight: normal;\n vertical-align: middle;\n cursor: pointer;\n}\n.radio-inline + .radio-inline,\n.checkbox-inline + .checkbox-inline {\n margin-top: 0;\n margin-left: 10px;\n}\ninput[type=\"radio\"][disabled],\ninput[type=\"checkbox\"][disabled],\ninput[type=\"radio\"].disabled,\ninput[type=\"checkbox\"].disabled,\nfieldset[disabled] input[type=\"radio\"],\nfieldset[disabled] input[type=\"checkbox\"] {\n cursor: not-allowed;\n}\n.radio-inline.disabled,\n.checkbox-inline.disabled,\nfieldset[disabled] .radio-inline,\nfieldset[disabled] .checkbox-inline {\n cursor: not-allowed;\n}\n.radio.disabled label,\n.checkbox.disabled label,\nfieldset[disabled] .radio label,\nfieldset[disabled] .checkbox label {\n cursor: not-allowed;\n}\n.form-control-static {\n min-height: 34px;\n padding-top: 7px;\n padding-bottom: 7px;\n margin-bottom: 0;\n}\n.form-control-static.input-lg,\n.form-control-static.input-sm {\n padding-right: 0;\n padding-left: 0;\n}\n.input-sm {\n height: 30px;\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\nselect.input-sm {\n height: 30px;\n line-height: 30px;\n}\ntextarea.input-sm,\nselect[multiple].input-sm {\n height: auto;\n}\n.form-group-sm .form-control {\n height: 30px;\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\n.form-group-sm select.form-control {\n height: 30px;\n line-height: 30px;\n}\n.form-group-sm textarea.form-control,\n.form-group-sm select[multiple].form-control {\n height: auto;\n}\n.form-group-sm .form-control-static {\n height: 30px;\n min-height: 32px;\n padding: 6px 10px;\n font-size: 12px;\n line-height: 1.5;\n}\n.input-lg {\n height: 46px;\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\nselect.input-lg {\n height: 46px;\n line-height: 46px;\n}\ntextarea.input-lg,\nselect[multiple].input-lg {\n height: auto;\n}\n.form-group-lg .form-control {\n height: 46px;\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\n.form-group-lg select.form-control {\n height: 46px;\n line-height: 46px;\n}\n.form-group-lg textarea.form-control,\n.form-group-lg select[multiple].form-control {\n height: auto;\n}\n.form-group-lg .form-control-static {\n height: 46px;\n min-height: 38px;\n padding: 11px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n}\n.has-feedback {\n position: relative;\n}\n.has-feedback .form-control {\n padding-right: 42.5px;\n}\n.form-control-feedback {\n position: absolute;\n top: 0;\n right: 0;\n z-index: 2;\n display: block;\n width: 34px;\n height: 34px;\n line-height: 34px;\n text-align: center;\n pointer-events: none;\n}\n.input-lg + .form-control-feedback,\n.input-group-lg + .form-control-feedback,\n.form-group-lg .form-control + .form-control-feedback {\n width: 46px;\n height: 46px;\n line-height: 46px;\n}\n.input-sm + .form-control-feedback,\n.input-group-sm + .form-control-feedback,\n.form-group-sm .form-control + .form-control-feedback {\n width: 30px;\n height: 30px;\n line-height: 30px;\n}\n.has-success .help-block,\n.has-success .control-label,\n.has-success .radio,\n.has-success .checkbox,\n.has-success .radio-inline,\n.has-success .checkbox-inline,\n.has-success.radio label,\n.has-success.checkbox label,\n.has-success.radio-inline label,\n.has-success.checkbox-inline label {\n color: #3c763d;\n}\n.has-success .form-control {\n border-color: #3c763d;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);\n}\n.has-success .form-control:focus {\n border-color: #2b542c;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168;\n}\n.has-success .input-group-addon {\n color: #3c763d;\n background-color: #dff0d8;\n border-color: #3c763d;\n}\n.has-success .form-control-feedback {\n color: #3c763d;\n}\n.has-warning .help-block,\n.has-warning .control-label,\n.has-warning .radio,\n.has-warning .checkbox,\n.has-warning .radio-inline,\n.has-warning .checkbox-inline,\n.has-warning.radio label,\n.has-warning.checkbox label,\n.has-warning.radio-inline label,\n.has-warning.checkbox-inline label {\n color: #8a6d3b;\n}\n.has-warning .form-control {\n border-color: #8a6d3b;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);\n}\n.has-warning .form-control:focus {\n border-color: #66512c;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b;\n}\n.has-warning .input-group-addon {\n color: #8a6d3b;\n background-color: #fcf8e3;\n border-color: #8a6d3b;\n}\n.has-warning .form-control-feedback {\n color: #8a6d3b;\n}\n.has-error .help-block,\n.has-error .control-label,\n.has-error .radio,\n.has-error .checkbox,\n.has-error .radio-inline,\n.has-error .checkbox-inline,\n.has-error.radio label,\n.has-error.checkbox label,\n.has-error.radio-inline label,\n.has-error.checkbox-inline label {\n color: #a94442;\n}\n.has-error .form-control {\n border-color: #a94442;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);\n}\n.has-error .form-control:focus {\n border-color: #843534;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483;\n}\n.has-error .input-group-addon {\n color: #a94442;\n background-color: #f2dede;\n border-color: #a94442;\n}\n.has-error .form-control-feedback {\n color: #a94442;\n}\n.has-feedback label ~ .form-control-feedback {\n top: 25px;\n}\n.has-feedback label.sr-only ~ .form-control-feedback {\n top: 0;\n}\n.help-block {\n display: block;\n margin-top: 5px;\n margin-bottom: 10px;\n color: #737373;\n}\n@media (min-width: 768px) {\n .form-inline .form-group {\n display: inline-block;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .form-inline .form-control {\n display: inline-block;\n width: auto;\n vertical-align: middle;\n }\n .form-inline .form-control-static {\n display: inline-block;\n }\n .form-inline .input-group {\n display: inline-table;\n vertical-align: middle;\n }\n .form-inline .input-group .input-group-addon,\n .form-inline .input-group .input-group-btn,\n .form-inline .input-group .form-control {\n width: auto;\n }\n .form-inline .input-group > .form-control {\n width: 100%;\n }\n .form-inline .control-label {\n margin-bottom: 0;\n vertical-align: middle;\n }\n .form-inline .radio,\n .form-inline .checkbox {\n display: inline-block;\n margin-top: 0;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .form-inline .radio label,\n .form-inline .checkbox label {\n padding-left: 0;\n }\n .form-inline .radio input[type=\"radio\"],\n .form-inline .checkbox input[type=\"checkbox\"] {\n position: relative;\n margin-left: 0;\n }\n .form-inline .has-feedback .form-control-feedback {\n top: 0;\n }\n}\n.form-horizontal .radio,\n.form-horizontal .checkbox,\n.form-horizontal .radio-inline,\n.form-horizontal .checkbox-inline {\n padding-top: 7px;\n margin-top: 0;\n margin-bottom: 0;\n}\n.form-horizontal .radio,\n.form-horizontal .checkbox {\n min-height: 27px;\n}\n.form-horizontal .form-group {\n margin-right: -15px;\n margin-left: -15px;\n}\n@media (min-width: 768px) {\n .form-horizontal .control-label {\n padding-top: 7px;\n margin-bottom: 0;\n text-align: right;\n }\n}\n.form-horizontal .has-feedback .form-control-feedback {\n right: 15px;\n}\n@media (min-width: 768px) {\n .form-horizontal .form-group-lg .control-label {\n padding-top: 11px;\n font-size: 18px;\n }\n}\n@media (min-width: 768px) {\n .form-horizontal .form-group-sm .control-label {\n padding-top: 6px;\n font-size: 12px;\n }\n}\n.btn {\n display: inline-block;\n padding: 6px 12px;\n margin-bottom: 0;\n font-size: 14px;\n font-weight: normal;\n line-height: 1.42857143;\n text-align: center;\n white-space: nowrap;\n vertical-align: middle;\n -ms-touch-action: manipulation;\n touch-action: manipulation;\n cursor: pointer;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n background-image: none;\n border: 1px solid transparent;\n border-radius: 4px;\n}\n.btn:focus,\n.btn:active:focus,\n.btn.active:focus,\n.btn.focus,\n.btn:active.focus,\n.btn.active.focus {\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\n.btn:hover,\n.btn:focus,\n.btn.focus {\n color: #333;\n text-decoration: none;\n}\n.btn:active,\n.btn.active {\n background-image: none;\n outline: 0;\n -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);\n box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);\n}\n.btn.disabled,\n.btn[disabled],\nfieldset[disabled] .btn {\n cursor: not-allowed;\n filter: alpha(opacity=65);\n -webkit-box-shadow: none;\n box-shadow: none;\n opacity: .65;\n}\na.btn.disabled,\nfieldset[disabled] a.btn {\n pointer-events: none;\n}\n.btn-default {\n color: #333;\n background-color: #fff;\n border-color: #ccc;\n}\n.btn-default:focus,\n.btn-default.focus {\n color: #333;\n background-color: #e6e6e6;\n border-color: #8c8c8c;\n}\n.btn-default:hover {\n color: #333;\n background-color: #e6e6e6;\n border-color: #adadad;\n}\n.btn-default:active,\n.btn-default.active,\n.open > .dropdown-toggle.btn-default {\n color: #333;\n background-color: #e6e6e6;\n border-color: #adadad;\n}\n.btn-default:active:hover,\n.btn-default.active:hover,\n.open > .dropdown-toggle.btn-default:hover,\n.btn-default:active:focus,\n.btn-default.active:focus,\n.open > .dropdown-toggle.btn-default:focus,\n.btn-default:active.focus,\n.btn-default.active.focus,\n.open > .dropdown-toggle.btn-default.focus {\n color: #333;\n background-color: #d4d4d4;\n border-color: #8c8c8c;\n}\n.btn-default:active,\n.btn-default.active,\n.open > .dropdown-toggle.btn-default {\n background-image: none;\n}\n.btn-default.disabled:hover,\n.btn-default[disabled]:hover,\nfieldset[disabled] .btn-default:hover,\n.btn-default.disabled:focus,\n.btn-default[disabled]:focus,\nfieldset[disabled] .btn-default:focus,\n.btn-default.disabled.focus,\n.btn-default[disabled].focus,\nfieldset[disabled] .btn-default.focus {\n background-color: #fff;\n border-color: #ccc;\n}\n.btn-default .badge {\n color: #fff;\n background-color: #333;\n}\n.btn-primary {\n color: #fff;\n background-color: #337ab7;\n border-color: #2e6da4;\n}\n.btn-primary:focus,\n.btn-primary.focus {\n color: #fff;\n background-color: #286090;\n border-color: #122b40;\n}\n.btn-primary:hover {\n color: #fff;\n background-color: #286090;\n border-color: #204d74;\n}\n.btn-primary:active,\n.btn-primary.active,\n.open > .dropdown-toggle.btn-primary {\n color: #fff;\n background-color: #286090;\n border-color: #204d74;\n}\n.btn-primary:active:hover,\n.btn-primary.active:hover,\n.open > .dropdown-toggle.btn-primary:hover,\n.btn-primary:active:focus,\n.btn-primary.active:focus,\n.open > .dropdown-toggle.btn-primary:focus,\n.btn-primary:active.focus,\n.btn-primary.active.focus,\n.open > .dropdown-toggle.btn-primary.focus {\n color: #fff;\n background-color: #204d74;\n border-color: #122b40;\n}\n.btn-primary:active,\n.btn-primary.active,\n.open > .dropdown-toggle.btn-primary {\n background-image: none;\n}\n.btn-primary.disabled:hover,\n.btn-primary[disabled]:hover,\nfieldset[disabled] .btn-primary:hover,\n.btn-primary.disabled:focus,\n.btn-primary[disabled]:focus,\nfieldset[disabled] .btn-primary:focus,\n.btn-primary.disabled.focus,\n.btn-primary[disabled].focus,\nfieldset[disabled] .btn-primary.focus {\n background-color: #337ab7;\n border-color: #2e6da4;\n}\n.btn-primary .badge {\n color: #337ab7;\n background-color: #fff;\n}\n.btn-success {\n color: #fff;\n background-color: #5cb85c;\n border-color: #4cae4c;\n}\n.btn-success:focus,\n.btn-success.focus {\n color: #fff;\n background-color: #449d44;\n border-color: #255625;\n}\n.btn-success:hover {\n color: #fff;\n background-color: #449d44;\n border-color: #398439;\n}\n.btn-success:active,\n.btn-success.active,\n.open > .dropdown-toggle.btn-success {\n color: #fff;\n background-color: #449d44;\n border-color: #398439;\n}\n.btn-success:active:hover,\n.btn-success.active:hover,\n.open > .dropdown-toggle.btn-success:hover,\n.btn-success:active:focus,\n.btn-success.active:focus,\n.open > .dropdown-toggle.btn-success:focus,\n.btn-success:active.focus,\n.btn-success.active.focus,\n.open > .dropdown-toggle.btn-success.focus {\n color: #fff;\n background-color: #398439;\n border-color: #255625;\n}\n.btn-success:active,\n.btn-success.active,\n.open > .dropdown-toggle.btn-success {\n background-image: none;\n}\n.btn-success.disabled:hover,\n.btn-success[disabled]:hover,\nfieldset[disabled] .btn-success:hover,\n.btn-success.disabled:focus,\n.btn-success[disabled]:focus,\nfieldset[disabled] .btn-success:focus,\n.btn-success.disabled.focus,\n.btn-success[disabled].focus,\nfieldset[disabled] .btn-success.focus {\n background-color: #5cb85c;\n border-color: #4cae4c;\n}\n.btn-success .badge {\n color: #5cb85c;\n background-color: #fff;\n}\n.btn-info {\n color: #fff;\n background-color: #5bc0de;\n border-color: #46b8da;\n}\n.btn-info:focus,\n.btn-info.focus {\n color: #fff;\n background-color: #31b0d5;\n border-color: #1b6d85;\n}\n.btn-info:hover {\n color: #fff;\n background-color: #31b0d5;\n border-color: #269abc;\n}\n.btn-info:active,\n.btn-info.active,\n.open > .dropdown-toggle.btn-info {\n color: #fff;\n background-color: #31b0d5;\n border-color: #269abc;\n}\n.btn-info:active:hover,\n.btn-info.active:hover,\n.open > .dropdown-toggle.btn-info:hover,\n.btn-info:active:focus,\n.btn-info.active:focus,\n.open > .dropdown-toggle.btn-info:focus,\n.btn-info:active.focus,\n.btn-info.active.focus,\n.open > .dropdown-toggle.btn-info.focus {\n color: #fff;\n background-color: #269abc;\n border-color: #1b6d85;\n}\n.btn-info:active,\n.btn-info.active,\n.open > .dropdown-toggle.btn-info {\n background-image: none;\n}\n.btn-info.disabled:hover,\n.btn-info[disabled]:hover,\nfieldset[disabled] .btn-info:hover,\n.btn-info.disabled:focus,\n.btn-info[disabled]:focus,\nfieldset[disabled] .btn-info:focus,\n.btn-info.disabled.focus,\n.btn-info[disabled].focus,\nfieldset[disabled] .btn-info.focus {\n background-color: #5bc0de;\n border-color: #46b8da;\n}\n.btn-info .badge {\n color: #5bc0de;\n background-color: #fff;\n}\n.btn-warning {\n color: #fff;\n background-color: #f0ad4e;\n border-color: #eea236;\n}\n.btn-warning:focus,\n.btn-warning.focus {\n color: #fff;\n background-color: #ec971f;\n border-color: #985f0d;\n}\n.btn-warning:hover {\n color: #fff;\n background-color: #ec971f;\n border-color: #d58512;\n}\n.btn-warning:active,\n.btn-warning.active,\n.open > .dropdown-toggle.btn-warning {\n color: #fff;\n background-color: #ec971f;\n border-color: #d58512;\n}\n.btn-warning:active:hover,\n.btn-warning.active:hover,\n.open > .dropdown-toggle.btn-warning:hover,\n.btn-warning:active:focus,\n.btn-warning.active:focus,\n.open > .dropdown-toggle.btn-warning:focus,\n.btn-warning:active.focus,\n.btn-warning.active.focus,\n.open > .dropdown-toggle.btn-warning.focus {\n color: #fff;\n background-color: #d58512;\n border-color: #985f0d;\n}\n.btn-warning:active,\n.btn-warning.active,\n.open > .dropdown-toggle.btn-warning {\n background-image: none;\n}\n.btn-warning.disabled:hover,\n.btn-warning[disabled]:hover,\nfieldset[disabled] .btn-warning:hover,\n.btn-warning.disabled:focus,\n.btn-warning[disabled]:focus,\nfieldset[disabled] .btn-warning:focus,\n.btn-warning.disabled.focus,\n.btn-warning[disabled].focus,\nfieldset[disabled] .btn-warning.focus {\n background-color: #f0ad4e;\n border-color: #eea236;\n}\n.btn-warning .badge {\n color: #f0ad4e;\n background-color: #fff;\n}\n.btn-danger {\n color: #fff;\n background-color: #d9534f;\n border-color: #d43f3a;\n}\n.btn-danger:focus,\n.btn-danger.focus {\n color: #fff;\n background-color: #c9302c;\n border-color: #761c19;\n}\n.btn-danger:hover {\n color: #fff;\n background-color: #c9302c;\n border-color: #ac2925;\n}\n.btn-danger:active,\n.btn-danger.active,\n.open > .dropdown-toggle.btn-danger {\n color: #fff;\n background-color: #c9302c;\n border-color: #ac2925;\n}\n.btn-danger:active:hover,\n.btn-danger.active:hover,\n.open > .dropdown-toggle.btn-danger:hover,\n.btn-danger:active:focus,\n.btn-danger.active:focus,\n.open > .dropdown-toggle.btn-danger:focus,\n.btn-danger:active.focus,\n.btn-danger.active.focus,\n.open > .dropdown-toggle.btn-danger.focus {\n color: #fff;\n background-color: #ac2925;\n border-color: #761c19;\n}\n.btn-danger:active,\n.btn-danger.active,\n.open > .dropdown-toggle.btn-danger {\n background-image: none;\n}\n.btn-danger.disabled:hover,\n.btn-danger[disabled]:hover,\nfieldset[disabled] .btn-danger:hover,\n.btn-danger.disabled:focus,\n.btn-danger[disabled]:focus,\nfieldset[disabled] .btn-danger:focus,\n.btn-danger.disabled.focus,\n.btn-danger[disabled].focus,\nfieldset[disabled] .btn-danger.focus {\n background-color: #d9534f;\n border-color: #d43f3a;\n}\n.btn-danger .badge {\n color: #d9534f;\n background-color: #fff;\n}\n.btn-link {\n font-weight: normal;\n color: #337ab7;\n border-radius: 0;\n}\n.btn-link,\n.btn-link:active,\n.btn-link.active,\n.btn-link[disabled],\nfieldset[disabled] .btn-link {\n background-color: transparent;\n -webkit-box-shadow: none;\n box-shadow: none;\n}\n.btn-link,\n.btn-link:hover,\n.btn-link:focus,\n.btn-link:active {\n border-color: transparent;\n}\n.btn-link:hover,\n.btn-link:focus {\n color: #23527c;\n text-decoration: underline;\n background-color: transparent;\n}\n.btn-link[disabled]:hover,\nfieldset[disabled] .btn-link:hover,\n.btn-link[disabled]:focus,\nfieldset[disabled] .btn-link:focus {\n color: #777;\n text-decoration: none;\n}\n.btn-lg,\n.btn-group-lg > .btn {\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\n.btn-sm,\n.btn-group-sm > .btn {\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\n.btn-xs,\n.btn-group-xs > .btn {\n padding: 1px 5px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\n.btn-block {\n display: block;\n width: 100%;\n}\n.btn-block + .btn-block {\n margin-top: 5px;\n}\ninput[type=\"submit\"].btn-block,\ninput[type=\"reset\"].btn-block,\ninput[type=\"button\"].btn-block {\n width: 100%;\n}\n.fade {\n opacity: 0;\n -webkit-transition: opacity .15s linear;\n -o-transition: opacity .15s linear;\n transition: opacity .15s linear;\n}\n.fade.in {\n opacity: 1;\n}\n.collapse {\n display: none;\n}\n.collapse.in {\n display: block;\n}\ntr.collapse.in {\n display: table-row;\n}\ntbody.collapse.in {\n display: table-row-group;\n}\n.collapsing {\n position: relative;\n height: 0;\n overflow: hidden;\n -webkit-transition-timing-function: ease;\n -o-transition-timing-function: ease;\n transition-timing-function: ease;\n -webkit-transition-duration: .35s;\n -o-transition-duration: .35s;\n transition-duration: .35s;\n -webkit-transition-property: height, visibility;\n -o-transition-property: height, visibility;\n transition-property: height, visibility;\n}\n.caret {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 2px;\n vertical-align: middle;\n border-top: 4px dashed;\n border-top: 4px solid \\9;\n border-right: 4px solid transparent;\n border-left: 4px solid transparent;\n}\n.dropup,\n.dropdown {\n position: relative;\n}\n.dropdown-toggle:focus {\n outline: 0;\n}\n.dropdown-menu {\n position: absolute;\n top: 100%;\n left: 0;\n z-index: 1000;\n display: none;\n float: left;\n min-width: 160px;\n padding: 5px 0;\n margin: 2px 0 0;\n font-size: 14px;\n text-align: left;\n list-style: none;\n background-color: #fff;\n -webkit-background-clip: padding-box;\n background-clip: padding-box;\n border: 1px solid #ccc;\n border: 1px solid rgba(0, 0, 0, .15);\n border-radius: 4px;\n -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175);\n box-shadow: 0 6px 12px rgba(0, 0, 0, .175);\n}\n.dropdown-menu.pull-right {\n right: 0;\n left: auto;\n}\n.dropdown-menu .divider {\n height: 1px;\n margin: 9px 0;\n overflow: hidden;\n background-color: #e5e5e5;\n}\n.dropdown-menu > li > a {\n display: block;\n padding: 3px 20px;\n clear: both;\n font-weight: normal;\n line-height: 1.42857143;\n color: #333;\n white-space: nowrap;\n}\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n color: #262626;\n text-decoration: none;\n background-color: #f5f5f5;\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n color: #fff;\n text-decoration: none;\n background-color: #337ab7;\n outline: 0;\n}\n.dropdown-menu > .disabled > a,\n.dropdown-menu > .disabled > a:hover,\n.dropdown-menu > .disabled > a:focus {\n color: #777;\n}\n.dropdown-menu > .disabled > a:hover,\n.dropdown-menu > .disabled > a:focus {\n text-decoration: none;\n cursor: not-allowed;\n background-color: transparent;\n background-image: none;\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n}\n.open > .dropdown-menu {\n display: block;\n}\n.open > a {\n outline: 0;\n}\n.dropdown-menu-right {\n right: 0;\n left: auto;\n}\n.dropdown-menu-left {\n right: auto;\n left: 0;\n}\n.dropdown-header {\n display: block;\n padding: 3px 20px;\n font-size: 12px;\n line-height: 1.42857143;\n color: #777;\n white-space: nowrap;\n}\n.dropdown-backdrop {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 990;\n}\n.pull-right > .dropdown-menu {\n right: 0;\n left: auto;\n}\n.dropup .caret,\n.navbar-fixed-bottom .dropdown .caret {\n content: \"\";\n border-top: 0;\n border-bottom: 4px dashed;\n border-bottom: 4px solid \\9;\n}\n.dropup .dropdown-menu,\n.navbar-fixed-bottom .dropdown .dropdown-menu {\n top: auto;\n bottom: 100%;\n margin-bottom: 2px;\n}\n@media (min-width: 768px) {\n .navbar-right .dropdown-menu {\n right: 0;\n left: auto;\n }\n .navbar-right .dropdown-menu-left {\n right: auto;\n left: 0;\n }\n}\n.btn-group,\n.btn-group-vertical {\n position: relative;\n display: inline-block;\n vertical-align: middle;\n}\n.btn-group > .btn,\n.btn-group-vertical > .btn {\n position: relative;\n float: left;\n}\n.btn-group > .btn:hover,\n.btn-group-vertical > .btn:hover,\n.btn-group > .btn:focus,\n.btn-group-vertical > .btn:focus,\n.btn-group > .btn:active,\n.btn-group-vertical > .btn:active,\n.btn-group > .btn.active,\n.btn-group-vertical > .btn.active {\n z-index: 2;\n}\n.btn-group .btn + .btn,\n.btn-group .btn + .btn-group,\n.btn-group .btn-group + .btn,\n.btn-group .btn-group + .btn-group {\n margin-left: -1px;\n}\n.btn-toolbar {\n margin-left: -5px;\n}\n.btn-toolbar .btn,\n.btn-toolbar .btn-group,\n.btn-toolbar .input-group {\n float: left;\n}\n.btn-toolbar > .btn,\n.btn-toolbar > .btn-group,\n.btn-toolbar > .input-group {\n margin-left: 5px;\n}\n.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {\n border-radius: 0;\n}\n.btn-group > .btn:first-child {\n margin-left: 0;\n}\n.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n.btn-group > .btn:last-child:not(:first-child),\n.btn-group > .dropdown-toggle:not(:first-child) {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n.btn-group > .btn-group {\n float: left;\n}\n.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {\n border-radius: 0;\n}\n.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child,\n.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n.btn-group .dropdown-toggle:active,\n.btn-group.open .dropdown-toggle {\n outline: 0;\n}\n.btn-group > .btn + .dropdown-toggle {\n padding-right: 8px;\n padding-left: 8px;\n}\n.btn-group > .btn-lg + .dropdown-toggle {\n padding-right: 12px;\n padding-left: 12px;\n}\n.btn-group.open .dropdown-toggle {\n -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);\n box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);\n}\n.btn-group.open .dropdown-toggle.btn-link {\n -webkit-box-shadow: none;\n box-shadow: none;\n}\n.btn .caret {\n margin-left: 0;\n}\n.btn-lg .caret {\n border-width: 5px 5px 0;\n border-bottom-width: 0;\n}\n.dropup .btn-lg .caret {\n border-width: 0 5px 5px;\n}\n.btn-group-vertical > .btn,\n.btn-group-vertical > .btn-group,\n.btn-group-vertical > .btn-group > .btn {\n display: block;\n float: none;\n width: 100%;\n max-width: 100%;\n}\n.btn-group-vertical > .btn-group > .btn {\n float: none;\n}\n.btn-group-vertical > .btn + .btn,\n.btn-group-vertical > .btn + .btn-group,\n.btn-group-vertical > .btn-group + .btn,\n.btn-group-vertical > .btn-group + .btn-group {\n margin-top: -1px;\n margin-left: 0;\n}\n.btn-group-vertical > .btn:not(:first-child):not(:last-child) {\n border-radius: 0;\n}\n.btn-group-vertical > .btn:first-child:not(:last-child) {\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n.btn-group-vertical > .btn:last-child:not(:first-child) {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n border-bottom-right-radius: 4px;\n border-bottom-left-radius: 4px;\n}\n.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {\n border-radius: 0;\n}\n.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child,\n.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle {\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n.btn-group-justified {\n display: table;\n width: 100%;\n table-layout: fixed;\n border-collapse: separate;\n}\n.btn-group-justified > .btn,\n.btn-group-justified > .btn-group {\n display: table-cell;\n float: none;\n width: 1%;\n}\n.btn-group-justified > .btn-group .btn {\n width: 100%;\n}\n.btn-group-justified > .btn-group .dropdown-menu {\n left: auto;\n}\n[data-toggle=\"buttons\"] > .btn input[type=\"radio\"],\n[data-toggle=\"buttons\"] > .btn-group > .btn input[type=\"radio\"],\n[data-toggle=\"buttons\"] > .btn input[type=\"checkbox\"],\n[data-toggle=\"buttons\"] > .btn-group > .btn input[type=\"checkbox\"] {\n position: absolute;\n clip: rect(0, 0, 0, 0);\n pointer-events: none;\n}\n.input-group {\n position: relative;\n display: table;\n border-collapse: separate;\n}\n.input-group[class*=\"col-\"] {\n float: none;\n padding-right: 0;\n padding-left: 0;\n}\n.input-group .form-control {\n position: relative;\n z-index: 2;\n float: left;\n width: 100%;\n margin-bottom: 0;\n}\n.input-group .form-control:focus {\n z-index: 3;\n}\n.input-group-lg > .form-control,\n.input-group-lg > .input-group-addon,\n.input-group-lg > .input-group-btn > .btn {\n height: 46px;\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\nselect.input-group-lg > .form-control,\nselect.input-group-lg > .input-group-addon,\nselect.input-group-lg > .input-group-btn > .btn {\n height: 46px;\n line-height: 46px;\n}\ntextarea.input-group-lg > .form-control,\ntextarea.input-group-lg > .input-group-addon,\ntextarea.input-group-lg > .input-group-btn > .btn,\nselect[multiple].input-group-lg > .form-control,\nselect[multiple].input-group-lg > .input-group-addon,\nselect[multiple].input-group-lg > .input-group-btn > .btn {\n height: auto;\n}\n.input-group-sm > .form-control,\n.input-group-sm > .input-group-addon,\n.input-group-sm > .input-group-btn > .btn {\n height: 30px;\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\nselect.input-group-sm > .form-control,\nselect.input-group-sm > .input-group-addon,\nselect.input-group-sm > .input-group-btn > .btn {\n height: 30px;\n line-height: 30px;\n}\ntextarea.input-group-sm > .form-control,\ntextarea.input-group-sm > .input-group-addon,\ntextarea.input-group-sm > .input-group-btn > .btn,\nselect[multiple].input-group-sm > .form-control,\nselect[multiple].input-group-sm > .input-group-addon,\nselect[multiple].input-group-sm > .input-group-btn > .btn {\n height: auto;\n}\n.input-group-addon,\n.input-group-btn,\n.input-group .form-control {\n display: table-cell;\n}\n.input-group-addon:not(:first-child):not(:last-child),\n.input-group-btn:not(:first-child):not(:last-child),\n.input-group .form-control:not(:first-child):not(:last-child) {\n border-radius: 0;\n}\n.input-group-addon,\n.input-group-btn {\n width: 1%;\n white-space: nowrap;\n vertical-align: middle;\n}\n.input-group-addon {\n padding: 6px 12px;\n font-size: 14px;\n font-weight: normal;\n line-height: 1;\n color: #555;\n text-align: center;\n background-color: #eee;\n border: 1px solid #ccc;\n border-radius: 4px;\n}\n.input-group-addon.input-sm {\n padding: 5px 10px;\n font-size: 12px;\n border-radius: 3px;\n}\n.input-group-addon.input-lg {\n padding: 10px 16px;\n font-size: 18px;\n border-radius: 6px;\n}\n.input-group-addon input[type=\"radio\"],\n.input-group-addon input[type=\"checkbox\"] {\n margin-top: 0;\n}\n.input-group .form-control:first-child,\n.input-group-addon:first-child,\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group > .btn,\n.input-group-btn:first-child > .dropdown-toggle,\n.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n.input-group-addon:first-child {\n border-right: 0;\n}\n.input-group .form-control:last-child,\n.input-group-addon:last-child,\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group > .btn,\n.input-group-btn:last-child > .dropdown-toggle,\n.input-group-btn:first-child > .btn:not(:first-child),\n.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n.input-group-addon:last-child {\n border-left: 0;\n}\n.input-group-btn {\n position: relative;\n font-size: 0;\n white-space: nowrap;\n}\n.input-group-btn > .btn {\n position: relative;\n}\n.input-group-btn > .btn + .btn {\n margin-left: -1px;\n}\n.input-group-btn > .btn:hover,\n.input-group-btn > .btn:focus,\n.input-group-btn > .btn:active {\n z-index: 2;\n}\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group {\n margin-right: -1px;\n}\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group {\n z-index: 2;\n margin-left: -1px;\n}\n.nav {\n padding-left: 0;\n margin-bottom: 0;\n list-style: none;\n}\n.nav > li {\n position: relative;\n display: block;\n}\n.nav > li > a {\n position: relative;\n display: block;\n padding: 10px 15px;\n}\n.nav > li > a:hover,\n.nav > li > a:focus {\n text-decoration: none;\n background-color: #eee;\n}\n.nav > li.disabled > a {\n color: #777;\n}\n.nav > li.disabled > a:hover,\n.nav > li.disabled > a:focus {\n color: #777;\n text-decoration: none;\n cursor: not-allowed;\n background-color: transparent;\n}\n.nav .open > a,\n.nav .open > a:hover,\n.nav .open > a:focus {\n background-color: #eee;\n border-color: #337ab7;\n}\n.nav .nav-divider {\n height: 1px;\n margin: 9px 0;\n overflow: hidden;\n background-color: #e5e5e5;\n}\n.nav > li > a > img {\n max-width: none;\n}\n.nav-tabs {\n border-bottom: 1px solid #ddd;\n}\n.nav-tabs > li {\n float: left;\n margin-bottom: -1px;\n}\n.nav-tabs > li > a {\n margin-right: 2px;\n line-height: 1.42857143;\n border: 1px solid transparent;\n border-radius: 4px 4px 0 0;\n}\n.nav-tabs > li > a:hover {\n border-color: #eee #eee #ddd;\n}\n.nav-tabs > li.active > a,\n.nav-tabs > li.active > a:hover,\n.nav-tabs > li.active > a:focus {\n color: #555;\n cursor: default;\n background-color: #fff;\n border: 1px solid #ddd;\n border-bottom-color: transparent;\n}\n.nav-tabs.nav-justified {\n width: 100%;\n border-bottom: 0;\n}\n.nav-tabs.nav-justified > li {\n float: none;\n}\n.nav-tabs.nav-justified > li > a {\n margin-bottom: 5px;\n text-align: center;\n}\n.nav-tabs.nav-justified > .dropdown .dropdown-menu {\n top: auto;\n left: auto;\n}\n@media (min-width: 768px) {\n .nav-tabs.nav-justified > li {\n display: table-cell;\n width: 1%;\n }\n .nav-tabs.nav-justified > li > a {\n margin-bottom: 0;\n }\n}\n.nav-tabs.nav-justified > li > a {\n margin-right: 0;\n border-radius: 4px;\n}\n.nav-tabs.nav-justified > .active > a,\n.nav-tabs.nav-justified > .active > a:hover,\n.nav-tabs.nav-justified > .active > a:focus {\n border: 1px solid #ddd;\n}\n@media (min-width: 768px) {\n .nav-tabs.nav-justified > li > a {\n border-bottom: 1px solid #ddd;\n border-radius: 4px 4px 0 0;\n }\n .nav-tabs.nav-justified > .active > a,\n .nav-tabs.nav-justified > .active > a:hover,\n .nav-tabs.nav-justified > .active > a:focus {\n border-bottom-color: #fff;\n }\n}\n.nav-pills > li {\n float: left;\n}\n.nav-pills > li > a {\n border-radius: 4px;\n}\n.nav-pills > li + li {\n margin-left: 2px;\n}\n.nav-pills > li.active > a,\n.nav-pills > li.active > a:hover,\n.nav-pills > li.active > a:focus {\n color: #fff;\n background-color: #337ab7;\n}\n.nav-stacked > li {\n float: none;\n}\n.nav-stacked > li + li {\n margin-top: 2px;\n margin-left: 0;\n}\n.nav-justified {\n width: 100%;\n}\n.nav-justified > li {\n float: none;\n}\n.nav-justified > li > a {\n margin-bottom: 5px;\n text-align: center;\n}\n.nav-justified > .dropdown .dropdown-menu {\n top: auto;\n left: auto;\n}\n@media (min-width: 768px) {\n .nav-justified > li {\n display: table-cell;\n width: 1%;\n }\n .nav-justified > li > a {\n margin-bottom: 0;\n }\n}\n.nav-tabs-justified {\n border-bottom: 0;\n}\n.nav-tabs-justified > li > a {\n margin-right: 0;\n border-radius: 4px;\n}\n.nav-tabs-justified > .active > a,\n.nav-tabs-justified > .active > a:hover,\n.nav-tabs-justified > .active > a:focus {\n border: 1px solid #ddd;\n}\n@media (min-width: 768px) {\n .nav-tabs-justified > li > a {\n border-bottom: 1px solid #ddd;\n border-radius: 4px 4px 0 0;\n }\n .nav-tabs-justified > .active > a,\n .nav-tabs-justified > .active > a:hover,\n .nav-tabs-justified > .active > a:focus {\n border-bottom-color: #fff;\n }\n}\n.tab-content > .tab-pane {\n display: none;\n}\n.tab-content > .active {\n display: block;\n}\n.nav-tabs .dropdown-menu {\n margin-top: -1px;\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n.navbar {\n position: relative;\n min-height: 50px;\n margin-bottom: 20px;\n border: 1px solid transparent;\n}\n@media (min-width: 768px) {\n .navbar {\n border-radius: 4px;\n }\n}\n@media (min-width: 768px) {\n .navbar-header {\n float: left;\n }\n}\n.navbar-collapse {\n padding-right: 15px;\n padding-left: 15px;\n overflow-x: visible;\n -webkit-overflow-scrolling: touch;\n border-top: 1px solid transparent;\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1);\n}\n.navbar-collapse.in {\n overflow-y: auto;\n}\n@media (min-width: 768px) {\n .navbar-collapse {\n width: auto;\n border-top: 0;\n -webkit-box-shadow: none;\n box-shadow: none;\n }\n .navbar-collapse.collapse {\n display: block !important;\n height: auto !important;\n padding-bottom: 0;\n overflow: visible !important;\n }\n .navbar-collapse.in {\n overflow-y: visible;\n }\n .navbar-fixed-top .navbar-collapse,\n .navbar-static-top .navbar-collapse,\n .navbar-fixed-bottom .navbar-collapse {\n padding-right: 0;\n padding-left: 0;\n }\n}\n.navbar-fixed-top .navbar-collapse,\n.navbar-fixed-bottom .navbar-collapse {\n max-height: 340px;\n}\n@media (max-device-width: 480px) and (orientation: landscape) {\n .navbar-fixed-top .navbar-collapse,\n .navbar-fixed-bottom .navbar-collapse {\n max-height: 200px;\n }\n}\n.container > .navbar-header,\n.container-fluid > .navbar-header,\n.container > .navbar-collapse,\n.container-fluid > .navbar-collapse {\n margin-right: -15px;\n margin-left: -15px;\n}\n@media (min-width: 768px) {\n .container > .navbar-header,\n .container-fluid > .navbar-header,\n .container > .navbar-collapse,\n .container-fluid > .navbar-collapse {\n margin-right: 0;\n margin-left: 0;\n }\n}\n.navbar-static-top {\n z-index: 1000;\n border-width: 0 0 1px;\n}\n@media (min-width: 768px) {\n .navbar-static-top {\n border-radius: 0;\n }\n}\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n position: fixed;\n right: 0;\n left: 0;\n z-index: 1030;\n}\n@media (min-width: 768px) {\n .navbar-fixed-top,\n .navbar-fixed-bottom {\n border-radius: 0;\n }\n}\n.navbar-fixed-top {\n top: 0;\n border-width: 0 0 1px;\n}\n.navbar-fixed-bottom {\n bottom: 0;\n margin-bottom: 0;\n border-width: 1px 0 0;\n}\n.navbar-brand {\n float: left;\n height: 50px;\n padding: 15px 15px;\n font-size: 18px;\n line-height: 20px;\n}\n.navbar-brand:hover,\n.navbar-brand:focus {\n text-decoration: none;\n}\n.navbar-brand > img {\n display: block;\n}\n@media (min-width: 768px) {\n .navbar > .container .navbar-brand,\n .navbar > .container-fluid .navbar-brand {\n margin-left: -15px;\n }\n}\n.navbar-toggle {\n position: relative;\n float: right;\n padding: 9px 10px;\n margin-top: 8px;\n margin-right: 15px;\n margin-bottom: 8px;\n background-color: transparent;\n background-image: none;\n border: 1px solid transparent;\n border-radius: 4px;\n}\n.navbar-toggle:focus {\n outline: 0;\n}\n.navbar-toggle .icon-bar {\n display: block;\n width: 22px;\n height: 2px;\n border-radius: 1px;\n}\n.navbar-toggle .icon-bar + .icon-bar {\n margin-top: 4px;\n}\n@media (min-width: 768px) {\n .navbar-toggle {\n display: none;\n }\n}\n.navbar-nav {\n margin: 7.5px -15px;\n}\n.navbar-nav > li > a {\n padding-top: 10px;\n padding-bottom: 10px;\n line-height: 20px;\n}\n@media (max-width: 767px) {\n .navbar-nav .open .dropdown-menu {\n position: static;\n float: none;\n width: auto;\n margin-top: 0;\n background-color: transparent;\n border: 0;\n -webkit-box-shadow: none;\n box-shadow: none;\n }\n .navbar-nav .open .dropdown-menu > li > a,\n .navbar-nav .open .dropdown-menu .dropdown-header {\n padding: 5px 15px 5px 25px;\n }\n .navbar-nav .open .dropdown-menu > li > a {\n line-height: 20px;\n }\n .navbar-nav .open .dropdown-menu > li > a:hover,\n .navbar-nav .open .dropdown-menu > li > a:focus {\n background-image: none;\n }\n}\n@media (min-width: 768px) {\n .navbar-nav {\n float: left;\n margin: 0;\n }\n .navbar-nav > li {\n float: left;\n }\n .navbar-nav > li > a {\n padding-top: 15px;\n padding-bottom: 15px;\n }\n}\n.navbar-form {\n padding: 10px 15px;\n margin-top: 8px;\n margin-right: -15px;\n margin-bottom: 8px;\n margin-left: -15px;\n border-top: 1px solid transparent;\n border-bottom: 1px solid transparent;\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1);\n}\n@media (min-width: 768px) {\n .navbar-form .form-group {\n display: inline-block;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .navbar-form .form-control {\n display: inline-block;\n width: auto;\n vertical-align: middle;\n }\n .navbar-form .form-control-static {\n display: inline-block;\n }\n .navbar-form .input-group {\n display: inline-table;\n vertical-align: middle;\n }\n .navbar-form .input-group .input-group-addon,\n .navbar-form .input-group .input-group-btn,\n .navbar-form .input-group .form-control {\n width: auto;\n }\n .navbar-form .input-group > .form-control {\n width: 100%;\n }\n .navbar-form .control-label {\n margin-bottom: 0;\n vertical-align: middle;\n }\n .navbar-form .radio,\n .navbar-form .checkbox {\n display: inline-block;\n margin-top: 0;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .navbar-form .radio label,\n .navbar-form .checkbox label {\n padding-left: 0;\n }\n .navbar-form .radio input[type=\"radio\"],\n .navbar-form .checkbox input[type=\"checkbox\"] {\n position: relative;\n margin-left: 0;\n }\n .navbar-form .has-feedback .form-control-feedback {\n top: 0;\n }\n}\n@media (max-width: 767px) {\n .navbar-form .form-group {\n margin-bottom: 5px;\n }\n .navbar-form .form-group:last-child {\n margin-bottom: 0;\n }\n}\n@media (min-width: 768px) {\n .navbar-form {\n width: auto;\n padding-top: 0;\n padding-bottom: 0;\n margin-right: 0;\n margin-left: 0;\n border: 0;\n -webkit-box-shadow: none;\n box-shadow: none;\n }\n}\n.navbar-nav > li > .dropdown-menu {\n margin-top: 0;\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {\n margin-bottom: 0;\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n.navbar-btn {\n margin-top: 8px;\n margin-bottom: 8px;\n}\n.navbar-btn.btn-sm {\n margin-top: 10px;\n margin-bottom: 10px;\n}\n.navbar-btn.btn-xs {\n margin-top: 14px;\n margin-bottom: 14px;\n}\n.navbar-text {\n margin-top: 15px;\n margin-bottom: 15px;\n}\n@media (min-width: 768px) {\n .navbar-text {\n float: left;\n margin-right: 15px;\n margin-left: 15px;\n }\n}\n@media (min-width: 768px) {\n .navbar-left {\n float: left !important;\n }\n .navbar-right {\n float: right !important;\n margin-right: -15px;\n }\n .navbar-right ~ .navbar-right {\n margin-right: 0;\n }\n}\n.navbar-default {\n background-color: #f8f8f8;\n border-color: #e7e7e7;\n}\n.navbar-default .navbar-brand {\n color: #777;\n}\n.navbar-default .navbar-brand:hover,\n.navbar-default .navbar-brand:focus {\n color: #5e5e5e;\n background-color: transparent;\n}\n.navbar-default .navbar-text {\n color: #777;\n}\n.navbar-default .navbar-nav > li > a {\n color: #777;\n}\n.navbar-default .navbar-nav > li > a:hover,\n.navbar-default .navbar-nav > li > a:focus {\n color: #333;\n background-color: transparent;\n}\n.navbar-default .navbar-nav > .active > a,\n.navbar-default .navbar-nav > .active > a:hover,\n.navbar-default .navbar-nav > .active > a:focus {\n color: #555;\n background-color: #e7e7e7;\n}\n.navbar-default .navbar-nav > .disabled > a,\n.navbar-default .navbar-nav > .disabled > a:hover,\n.navbar-default .navbar-nav > .disabled > a:focus {\n color: #ccc;\n background-color: transparent;\n}\n.navbar-default .navbar-toggle {\n border-color: #ddd;\n}\n.navbar-default .navbar-toggle:hover,\n.navbar-default .navbar-toggle:focus {\n background-color: #ddd;\n}\n.navbar-default .navbar-toggle .icon-bar {\n background-color: #888;\n}\n.navbar-default .navbar-collapse,\n.navbar-default .navbar-form {\n border-color: #e7e7e7;\n}\n.navbar-default .navbar-nav > .open > a,\n.navbar-default .navbar-nav > .open > a:hover,\n.navbar-default .navbar-nav > .open > a:focus {\n color: #555;\n background-color: #e7e7e7;\n}\n@media (max-width: 767px) {\n .navbar-default .navbar-nav .open .dropdown-menu > li > a {\n color: #777;\n }\n .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover,\n .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus {\n color: #333;\n background-color: transparent;\n }\n .navbar-default .navbar-nav .open .dropdown-menu > .active > a,\n .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover,\n .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus {\n color: #555;\n background-color: #e7e7e7;\n }\n .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a,\n .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover,\n .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus {\n color: #ccc;\n background-color: transparent;\n }\n}\n.navbar-default .navbar-link {\n color: #777;\n}\n.navbar-default .navbar-link:hover {\n color: #333;\n}\n.navbar-default .btn-link {\n color: #777;\n}\n.navbar-default .btn-link:hover,\n.navbar-default .btn-link:focus {\n color: #333;\n}\n.navbar-default .btn-link[disabled]:hover,\nfieldset[disabled] .navbar-default .btn-link:hover,\n.navbar-default .btn-link[disabled]:focus,\nfieldset[disabled] .navbar-default .btn-link:focus {\n color: #ccc;\n}\n.navbar-inverse {\n background-color: #222;\n border-color: #080808;\n}\n.navbar-inverse .navbar-brand {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-brand:hover,\n.navbar-inverse .navbar-brand:focus {\n color: #fff;\n background-color: transparent;\n}\n.navbar-inverse .navbar-text {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-nav > li > a {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-nav > li > a:hover,\n.navbar-inverse .navbar-nav > li > a:focus {\n color: #fff;\n background-color: transparent;\n}\n.navbar-inverse .navbar-nav > .active > a,\n.navbar-inverse .navbar-nav > .active > a:hover,\n.navbar-inverse .navbar-nav > .active > a:focus {\n color: #fff;\n background-color: #080808;\n}\n.navbar-inverse .navbar-nav > .disabled > a,\n.navbar-inverse .navbar-nav > .disabled > a:hover,\n.navbar-inverse .navbar-nav > .disabled > a:focus {\n color: #444;\n background-color: transparent;\n}\n.navbar-inverse .navbar-toggle {\n border-color: #333;\n}\n.navbar-inverse .navbar-toggle:hover,\n.navbar-inverse .navbar-toggle:focus {\n background-color: #333;\n}\n.navbar-inverse .navbar-toggle .icon-bar {\n background-color: #fff;\n}\n.navbar-inverse .navbar-collapse,\n.navbar-inverse .navbar-form {\n border-color: #101010;\n}\n.navbar-inverse .navbar-nav > .open > a,\n.navbar-inverse .navbar-nav > .open > a:hover,\n.navbar-inverse .navbar-nav > .open > a:focus {\n color: #fff;\n background-color: #080808;\n}\n@media (max-width: 767px) {\n .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header {\n border-color: #080808;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu .divider {\n background-color: #080808;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > li > a {\n color: #9d9d9d;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover,\n .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus {\n color: #fff;\n background-color: transparent;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus {\n color: #fff;\n background-color: #080808;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus {\n color: #444;\n background-color: transparent;\n }\n}\n.navbar-inverse .navbar-link {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-link:hover {\n color: #fff;\n}\n.navbar-inverse .btn-link {\n color: #9d9d9d;\n}\n.navbar-inverse .btn-link:hover,\n.navbar-inverse .btn-link:focus {\n color: #fff;\n}\n.navbar-inverse .btn-link[disabled]:hover,\nfieldset[disabled] .navbar-inverse .btn-link:hover,\n.navbar-inverse .btn-link[disabled]:focus,\nfieldset[disabled] .navbar-inverse .btn-link:focus {\n color: #444;\n}\n.breadcrumb {\n padding: 8px 15px;\n margin-bottom: 20px;\n list-style: none;\n background-color: #f5f5f5;\n border-radius: 4px;\n}\n.breadcrumb > li {\n display: inline-block;\n}\n.breadcrumb > li + li:before {\n padding: 0 5px;\n color: #ccc;\n content: \"/\\00a0\";\n}\n.breadcrumb > .active {\n color: #777;\n}\n.pagination {\n display: inline-block;\n padding-left: 0;\n margin: 20px 0;\n border-radius: 4px;\n}\n.pagination > li {\n display: inline;\n}\n.pagination > li > a,\n.pagination > li > span {\n position: relative;\n float: left;\n padding: 6px 12px;\n margin-left: -1px;\n line-height: 1.42857143;\n color: #337ab7;\n text-decoration: none;\n background-color: #fff;\n border: 1px solid #ddd;\n}\n.pagination > li:first-child > a,\n.pagination > li:first-child > span {\n margin-left: 0;\n border-top-left-radius: 4px;\n border-bottom-left-radius: 4px;\n}\n.pagination > li:last-child > a,\n.pagination > li:last-child > span {\n border-top-right-radius: 4px;\n border-bottom-right-radius: 4px;\n}\n.pagination > li > a:hover,\n.pagination > li > span:hover,\n.pagination > li > a:focus,\n.pagination > li > span:focus {\n z-index: 2;\n color: #23527c;\n background-color: #eee;\n border-color: #ddd;\n}\n.pagination > .active > a,\n.pagination > .active > span,\n.pagination > .active > a:hover,\n.pagination > .active > span:hover,\n.pagination > .active > a:focus,\n.pagination > .active > span:focus {\n z-index: 3;\n color: #fff;\n cursor: default;\n background-color: #337ab7;\n border-color: #337ab7;\n}\n.pagination > .disabled > span,\n.pagination > .disabled > span:hover,\n.pagination > .disabled > span:focus,\n.pagination > .disabled > a,\n.pagination > .disabled > a:hover,\n.pagination > .disabled > a:focus {\n color: #777;\n cursor: not-allowed;\n background-color: #fff;\n border-color: #ddd;\n}\n.pagination-lg > li > a,\n.pagination-lg > li > span {\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n}\n.pagination-lg > li:first-child > a,\n.pagination-lg > li:first-child > span {\n border-top-left-radius: 6px;\n border-bottom-left-radius: 6px;\n}\n.pagination-lg > li:last-child > a,\n.pagination-lg > li:last-child > span {\n border-top-right-radius: 6px;\n border-bottom-right-radius: 6px;\n}\n.pagination-sm > li > a,\n.pagination-sm > li > span {\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n}\n.pagination-sm > li:first-child > a,\n.pagination-sm > li:first-child > span {\n border-top-left-radius: 3px;\n border-bottom-left-radius: 3px;\n}\n.pagination-sm > li:last-child > a,\n.pagination-sm > li:last-child > span {\n border-top-right-radius: 3px;\n border-bottom-right-radius: 3px;\n}\n.pager {\n padding-left: 0;\n margin: 20px 0;\n text-align: center;\n list-style: none;\n}\n.pager li {\n display: inline;\n}\n.pager li > a,\n.pager li > span {\n display: inline-block;\n padding: 5px 14px;\n background-color: #fff;\n border: 1px solid #ddd;\n border-radius: 15px;\n}\n.pager li > a:hover,\n.pager li > a:focus {\n text-decoration: none;\n background-color: #eee;\n}\n.pager .next > a,\n.pager .next > span {\n float: right;\n}\n.pager .previous > a,\n.pager .previous > span {\n float: left;\n}\n.pager .disabled > a,\n.pager .disabled > a:hover,\n.pager .disabled > a:focus,\n.pager .disabled > span {\n color: #777;\n cursor: not-allowed;\n background-color: #fff;\n}\n.label {\n display: inline;\n padding: .2em .6em .3em;\n font-size: 75%;\n font-weight: bold;\n line-height: 1;\n color: #fff;\n text-align: center;\n white-space: nowrap;\n vertical-align: baseline;\n border-radius: .25em;\n}\na.label:hover,\na.label:focus {\n color: #fff;\n text-decoration: none;\n cursor: pointer;\n}\n.label:empty {\n display: none;\n}\n.btn .label {\n position: relative;\n top: -1px;\n}\n.label-default {\n background-color: #777;\n}\n.label-default[href]:hover,\n.label-default[href]:focus {\n background-color: #5e5e5e;\n}\n.label-primary {\n background-color: #337ab7;\n}\n.label-primary[href]:hover,\n.label-primary[href]:focus {\n background-color: #286090;\n}\n.label-success {\n background-color: #5cb85c;\n}\n.label-success[href]:hover,\n.label-success[href]:focus {\n background-color: #449d44;\n}\n.label-info {\n background-color: #5bc0de;\n}\n.label-info[href]:hover,\n.label-info[href]:focus {\n background-color: #31b0d5;\n}\n.label-warning {\n background-color: #f0ad4e;\n}\n.label-warning[href]:hover,\n.label-warning[href]:focus {\n background-color: #ec971f;\n}\n.label-danger {\n background-color: #d9534f;\n}\n.label-danger[href]:hover,\n.label-danger[href]:focus {\n background-color: #c9302c;\n}\n.badge {\n display: inline-block;\n min-width: 10px;\n padding: 3px 7px;\n font-size: 12px;\n font-weight: bold;\n line-height: 1;\n color: #fff;\n text-align: center;\n white-space: nowrap;\n vertical-align: middle;\n background-color: #777;\n border-radius: 10px;\n}\n.badge:empty {\n display: none;\n}\n.btn .badge {\n position: relative;\n top: -1px;\n}\n.btn-xs .badge,\n.btn-group-xs > .btn .badge {\n top: 0;\n padding: 1px 5px;\n}\na.badge:hover,\na.badge:focus {\n color: #fff;\n text-decoration: none;\n cursor: pointer;\n}\n.list-group-item.active > .badge,\n.nav-pills > .active > a > .badge {\n color: #337ab7;\n background-color: #fff;\n}\n.list-group-item > .badge {\n float: right;\n}\n.list-group-item > .badge + .badge {\n margin-right: 5px;\n}\n.nav-pills > li > a > .badge {\n margin-left: 3px;\n}\n.jumbotron {\n padding-top: 30px;\n padding-bottom: 30px;\n margin-bottom: 30px;\n color: inherit;\n background-color: #eee;\n}\n.jumbotron h1,\n.jumbotron .h1 {\n color: inherit;\n}\n.jumbotron p {\n margin-bottom: 15px;\n font-size: 21px;\n font-weight: 200;\n}\n.jumbotron > hr {\n border-top-color: #d5d5d5;\n}\n.container .jumbotron,\n.container-fluid .jumbotron {\n padding-right: 15px;\n padding-left: 15px;\n border-radius: 6px;\n}\n.jumbotron .container {\n max-width: 100%;\n}\n@media screen and (min-width: 768px) {\n .jumbotron {\n padding-top: 48px;\n padding-bottom: 48px;\n }\n .container .jumbotron,\n .container-fluid .jumbotron {\n padding-right: 60px;\n padding-left: 60px;\n }\n .jumbotron h1,\n .jumbotron .h1 {\n font-size: 63px;\n }\n}\n.thumbnail {\n display: block;\n padding: 4px;\n margin-bottom: 20px;\n line-height: 1.42857143;\n background-color: #fff;\n border: 1px solid #ddd;\n border-radius: 4px;\n -webkit-transition: border .2s ease-in-out;\n -o-transition: border .2s ease-in-out;\n transition: border .2s ease-in-out;\n}\n.thumbnail > img,\n.thumbnail a > img {\n margin-right: auto;\n margin-left: auto;\n}\na.thumbnail:hover,\na.thumbnail:focus,\na.thumbnail.active {\n border-color: #337ab7;\n}\n.thumbnail .caption {\n padding: 9px;\n color: #333;\n}\n.alert {\n padding: 15px;\n margin-bottom: 20px;\n border: 1px solid transparent;\n border-radius: 4px;\n}\n.alert h4 {\n margin-top: 0;\n color: inherit;\n}\n.alert .alert-link {\n font-weight: bold;\n}\n.alert > p,\n.alert > ul {\n margin-bottom: 0;\n}\n.alert > p + p {\n margin-top: 5px;\n}\n.alert-dismissable,\n.alert-dismissible {\n padding-right: 35px;\n}\n.alert-dismissable .close,\n.alert-dismissible .close {\n position: relative;\n top: -2px;\n right: -21px;\n color: inherit;\n}\n.alert-success {\n color: #3c763d;\n background-color: #dff0d8;\n border-color: #d6e9c6;\n}\n.alert-success hr {\n border-top-color: #c9e2b3;\n}\n.alert-success .alert-link {\n color: #2b542c;\n}\n.alert-info {\n color: #31708f;\n background-color: #d9edf7;\n border-color: #bce8f1;\n}\n.alert-info hr {\n border-top-color: #a6e1ec;\n}\n.alert-info .alert-link {\n color: #245269;\n}\n.alert-warning {\n color: #8a6d3b;\n background-color: #fcf8e3;\n border-color: #faebcc;\n}\n.alert-warning hr {\n border-top-color: #f7e1b5;\n}\n.alert-warning .alert-link {\n color: #66512c;\n}\n.alert-danger {\n color: #a94442;\n background-color: #f2dede;\n border-color: #ebccd1;\n}\n.alert-danger hr {\n border-top-color: #e4b9c0;\n}\n.alert-danger .alert-link {\n color: #843534;\n}\n@-webkit-keyframes progress-bar-stripes {\n from {\n background-position: 40px 0;\n }\n to {\n background-position: 0 0;\n }\n}\n@-o-keyframes progress-bar-stripes {\n from {\n background-position: 40px 0;\n }\n to {\n background-position: 0 0;\n }\n}\n@keyframes progress-bar-stripes {\n from {\n background-position: 40px 0;\n }\n to {\n background-position: 0 0;\n }\n}\n.progress {\n height: 20px;\n margin-bottom: 20px;\n overflow: hidden;\n background-color: #f5f5f5;\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1);\n box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1);\n}\n.progress-bar {\n float: left;\n width: 0;\n height: 100%;\n font-size: 12px;\n line-height: 20px;\n color: #fff;\n text-align: center;\n background-color: #337ab7;\n -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15);\n box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15);\n -webkit-transition: width .6s ease;\n -o-transition: width .6s ease;\n transition: width .6s ease;\n}\n.progress-striped .progress-bar,\n.progress-bar-striped {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);\n -webkit-background-size: 40px 40px;\n background-size: 40px 40px;\n}\n.progress.active .progress-bar,\n.progress-bar.active {\n -webkit-animation: progress-bar-stripes 2s linear infinite;\n -o-animation: progress-bar-stripes 2s linear infinite;\n animation: progress-bar-stripes 2s linear infinite;\n}\n.progress-bar-success {\n background-color: #5cb85c;\n}\n.progress-striped .progress-bar-success {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);\n}\n.progress-bar-info {\n background-color: #5bc0de;\n}\n.progress-striped .progress-bar-info {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);\n}\n.progress-bar-warning {\n background-color: #f0ad4e;\n}\n.progress-striped .progress-bar-warning {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);\n}\n.progress-bar-danger {\n background-color: #d9534f;\n}\n.progress-striped .progress-bar-danger {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);\n}\n.media {\n margin-top: 15px;\n}\n.media:first-child {\n margin-top: 0;\n}\n.media,\n.media-body {\n overflow: hidden;\n zoom: 1;\n}\n.media-body {\n width: 10000px;\n}\n.media-object {\n display: block;\n}\n.media-object.img-thumbnail {\n max-width: none;\n}\n.media-right,\n.media > .pull-right {\n padding-left: 10px;\n}\n.media-left,\n.media > .pull-left {\n padding-right: 10px;\n}\n.media-left,\n.media-right,\n.media-body {\n display: table-cell;\n vertical-align: top;\n}\n.media-middle {\n vertical-align: middle;\n}\n.media-bottom {\n vertical-align: bottom;\n}\n.media-heading {\n margin-top: 0;\n margin-bottom: 5px;\n}\n.media-list {\n padding-left: 0;\n list-style: none;\n}\n.list-group {\n padding-left: 0;\n margin-bottom: 20px;\n}\n.list-group-item {\n position: relative;\n display: block;\n padding: 10px 15px;\n margin-bottom: -1px;\n background-color: #fff;\n border: 1px solid #ddd;\n}\n.list-group-item:first-child {\n border-top-left-radius: 4px;\n border-top-right-radius: 4px;\n}\n.list-group-item:last-child {\n margin-bottom: 0;\n border-bottom-right-radius: 4px;\n border-bottom-left-radius: 4px;\n}\na.list-group-item,\nbutton.list-group-item {\n color: #555;\n}\na.list-group-item .list-group-item-heading,\nbutton.list-group-item .list-group-item-heading {\n color: #333;\n}\na.list-group-item:hover,\nbutton.list-group-item:hover,\na.list-group-item:focus,\nbutton.list-group-item:focus {\n color: #555;\n text-decoration: none;\n background-color: #f5f5f5;\n}\nbutton.list-group-item {\n width: 100%;\n text-align: left;\n}\n.list-group-item.disabled,\n.list-group-item.disabled:hover,\n.list-group-item.disabled:focus {\n color: #777;\n cursor: not-allowed;\n background-color: #eee;\n}\n.list-group-item.disabled .list-group-item-heading,\n.list-group-item.disabled:hover .list-group-item-heading,\n.list-group-item.disabled:focus .list-group-item-heading {\n color: inherit;\n}\n.list-group-item.disabled .list-group-item-text,\n.list-group-item.disabled:hover .list-group-item-text,\n.list-group-item.disabled:focus .list-group-item-text {\n color: #777;\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n z-index: 2;\n color: #fff;\n background-color: #337ab7;\n border-color: #337ab7;\n}\n.list-group-item.active .list-group-item-heading,\n.list-group-item.active:hover .list-group-item-heading,\n.list-group-item.active:focus .list-group-item-heading,\n.list-group-item.active .list-group-item-heading > small,\n.list-group-item.active:hover .list-group-item-heading > small,\n.list-group-item.active:focus .list-group-item-heading > small,\n.list-group-item.active .list-group-item-heading > .small,\n.list-group-item.active:hover .list-group-item-heading > .small,\n.list-group-item.active:focus .list-group-item-heading > .small {\n color: inherit;\n}\n.list-group-item.active .list-group-item-text,\n.list-group-item.active:hover .list-group-item-text,\n.list-group-item.active:focus .list-group-item-text {\n color: #c7ddef;\n}\n.list-group-item-success {\n color: #3c763d;\n background-color: #dff0d8;\n}\na.list-group-item-success,\nbutton.list-group-item-success {\n color: #3c763d;\n}\na.list-group-item-success .list-group-item-heading,\nbutton.list-group-item-success .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-success:hover,\nbutton.list-group-item-success:hover,\na.list-group-item-success:focus,\nbutton.list-group-item-success:focus {\n color: #3c763d;\n background-color: #d0e9c6;\n}\na.list-group-item-success.active,\nbutton.list-group-item-success.active,\na.list-group-item-success.active:hover,\nbutton.list-group-item-success.active:hover,\na.list-group-item-success.active:focus,\nbutton.list-group-item-success.active:focus {\n color: #fff;\n background-color: #3c763d;\n border-color: #3c763d;\n}\n.list-group-item-info {\n color: #31708f;\n background-color: #d9edf7;\n}\na.list-group-item-info,\nbutton.list-group-item-info {\n color: #31708f;\n}\na.list-group-item-info .list-group-item-heading,\nbutton.list-group-item-info .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-info:hover,\nbutton.list-group-item-info:hover,\na.list-group-item-info:focus,\nbutton.list-group-item-info:focus {\n color: #31708f;\n background-color: #c4e3f3;\n}\na.list-group-item-info.active,\nbutton.list-group-item-info.active,\na.list-group-item-info.active:hover,\nbutton.list-group-item-info.active:hover,\na.list-group-item-info.active:focus,\nbutton.list-group-item-info.active:focus {\n color: #fff;\n background-color: #31708f;\n border-color: #31708f;\n}\n.list-group-item-warning {\n color: #8a6d3b;\n background-color: #fcf8e3;\n}\na.list-group-item-warning,\nbutton.list-group-item-warning {\n color: #8a6d3b;\n}\na.list-group-item-warning .list-group-item-heading,\nbutton.list-group-item-warning .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-warning:hover,\nbutton.list-group-item-warning:hover,\na.list-group-item-warning:focus,\nbutton.list-group-item-warning:focus {\n color: #8a6d3b;\n background-color: #faf2cc;\n}\na.list-group-item-warning.active,\nbutton.list-group-item-warning.active,\na.list-group-item-warning.active:hover,\nbutton.list-group-item-warning.active:hover,\na.list-group-item-warning.active:focus,\nbutton.list-group-item-warning.active:focus {\n color: #fff;\n background-color: #8a6d3b;\n border-color: #8a6d3b;\n}\n.list-group-item-danger {\n color: #a94442;\n background-color: #f2dede;\n}\na.list-group-item-danger,\nbutton.list-group-item-danger {\n color: #a94442;\n}\na.list-group-item-danger .list-group-item-heading,\nbutton.list-group-item-danger .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-danger:hover,\nbutton.list-group-item-danger:hover,\na.list-group-item-danger:focus,\nbutton.list-group-item-danger:focus {\n color: #a94442;\n background-color: #ebcccc;\n}\na.list-group-item-danger.active,\nbutton.list-group-item-danger.active,\na.list-group-item-danger.active:hover,\nbutton.list-group-item-danger.active:hover,\na.list-group-item-danger.active:focus,\nbutton.list-group-item-danger.active:focus {\n color: #fff;\n background-color: #a94442;\n border-color: #a94442;\n}\n.list-group-item-heading {\n margin-top: 0;\n margin-bottom: 5px;\n}\n.list-group-item-text {\n margin-bottom: 0;\n line-height: 1.3;\n}\n.panel {\n margin-bottom: 20px;\n background-color: #fff;\n border: 1px solid transparent;\n border-radius: 4px;\n -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05);\n box-shadow: 0 1px 1px rgba(0, 0, 0, .05);\n}\n.panel-body {\n padding: 15px;\n}\n.panel-heading {\n padding: 10px 15px;\n border-bottom: 1px solid transparent;\n border-top-left-radius: 3px;\n border-top-right-radius: 3px;\n}\n.panel-heading > .dropdown .dropdown-toggle {\n color: inherit;\n}\n.panel-title {\n margin-top: 0;\n margin-bottom: 0;\n font-size: 16px;\n color: inherit;\n}\n.panel-title > a,\n.panel-title > small,\n.panel-title > .small,\n.panel-title > small > a,\n.panel-title > .small > a {\n color: inherit;\n}\n.panel-footer {\n padding: 10px 15px;\n background-color: #f5f5f5;\n border-top: 1px solid #ddd;\n border-bottom-right-radius: 3px;\n border-bottom-left-radius: 3px;\n}\n.panel > .list-group,\n.panel > .panel-collapse > .list-group {\n margin-bottom: 0;\n}\n.panel > .list-group .list-group-item,\n.panel > .panel-collapse > .list-group .list-group-item {\n border-width: 1px 0;\n border-radius: 0;\n}\n.panel > .list-group:first-child .list-group-item:first-child,\n.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child {\n border-top: 0;\n border-top-left-radius: 3px;\n border-top-right-radius: 3px;\n}\n.panel > .list-group:last-child .list-group-item:last-child,\n.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child {\n border-bottom: 0;\n border-bottom-right-radius: 3px;\n border-bottom-left-radius: 3px;\n}\n.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n.panel-heading + .list-group .list-group-item:first-child {\n border-top-width: 0;\n}\n.list-group + .panel-footer {\n border-top-width: 0;\n}\n.panel > .table,\n.panel > .table-responsive > .table,\n.panel > .panel-collapse > .table {\n margin-bottom: 0;\n}\n.panel > .table caption,\n.panel > .table-responsive > .table caption,\n.panel > .panel-collapse > .table caption {\n padding-right: 15px;\n padding-left: 15px;\n}\n.panel > .table:first-child,\n.panel > .table-responsive:first-child > .table:first-child {\n border-top-left-radius: 3px;\n border-top-right-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child {\n border-top-left-radius: 3px;\n border-top-right-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child td:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child,\n.panel > .table:first-child > thead:first-child > tr:first-child th:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child {\n border-top-left-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child td:last-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child,\n.panel > .table:first-child > thead:first-child > tr:first-child th:last-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child {\n border-top-right-radius: 3px;\n}\n.panel > .table:last-child,\n.panel > .table-responsive:last-child > .table:last-child {\n border-bottom-right-radius: 3px;\n border-bottom-left-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child {\n border-bottom-right-radius: 3px;\n border-bottom-left-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child,\n.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child {\n border-bottom-left-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child,\n.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child {\n border-bottom-right-radius: 3px;\n}\n.panel > .panel-body + .table,\n.panel > .panel-body + .table-responsive,\n.panel > .table + .panel-body,\n.panel > .table-responsive + .panel-body {\n border-top: 1px solid #ddd;\n}\n.panel > .table > tbody:first-child > tr:first-child th,\n.panel > .table > tbody:first-child > tr:first-child td {\n border-top: 0;\n}\n.panel > .table-bordered,\n.panel > .table-responsive > .table-bordered {\n border: 0;\n}\n.panel > .table-bordered > thead > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > thead > tr > th:first-child,\n.panel > .table-bordered > tbody > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child,\n.panel > .table-bordered > tfoot > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child,\n.panel > .table-bordered > thead > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > thead > tr > td:first-child,\n.panel > .table-bordered > tbody > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child,\n.panel > .table-bordered > tfoot > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child {\n border-left: 0;\n}\n.panel > .table-bordered > thead > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > thead > tr > th:last-child,\n.panel > .table-bordered > tbody > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child,\n.panel > .table-bordered > tfoot > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child,\n.panel > .table-bordered > thead > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > thead > tr > td:last-child,\n.panel > .table-bordered > tbody > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child,\n.panel > .table-bordered > tfoot > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child {\n border-right: 0;\n}\n.panel > .table-bordered > thead > tr:first-child > td,\n.panel > .table-responsive > .table-bordered > thead > tr:first-child > td,\n.panel > .table-bordered > tbody > tr:first-child > td,\n.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td,\n.panel > .table-bordered > thead > tr:first-child > th,\n.panel > .table-responsive > .table-bordered > thead > tr:first-child > th,\n.panel > .table-bordered > tbody > tr:first-child > th,\n.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th {\n border-bottom: 0;\n}\n.panel > .table-bordered > tbody > tr:last-child > td,\n.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td,\n.panel > .table-bordered > tfoot > tr:last-child > td,\n.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td,\n.panel > .table-bordered > tbody > tr:last-child > th,\n.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th,\n.panel > .table-bordered > tfoot > tr:last-child > th,\n.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th {\n border-bottom: 0;\n}\n.panel > .table-responsive {\n margin-bottom: 0;\n border: 0;\n}\n.panel-group {\n margin-bottom: 20px;\n}\n.panel-group .panel {\n margin-bottom: 0;\n border-radius: 4px;\n}\n.panel-group .panel + .panel {\n margin-top: 5px;\n}\n.panel-group .panel-heading {\n border-bottom: 0;\n}\n.panel-group .panel-heading + .panel-collapse > .panel-body,\n.panel-group .panel-heading + .panel-collapse > .list-group {\n border-top: 1px solid #ddd;\n}\n.panel-group .panel-footer {\n border-top: 0;\n}\n.panel-group .panel-footer + .panel-collapse .panel-body {\n border-bottom: 1px solid #ddd;\n}\n.panel-default {\n border-color: #ddd;\n}\n.panel-default > .panel-heading {\n color: #333;\n background-color: #f5f5f5;\n border-color: #ddd;\n}\n.panel-default > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #ddd;\n}\n.panel-default > .panel-heading .badge {\n color: #f5f5f5;\n background-color: #333;\n}\n.panel-default > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #ddd;\n}\n.panel-primary {\n border-color: #337ab7;\n}\n.panel-primary > .panel-heading {\n color: #fff;\n background-color: #337ab7;\n border-color: #337ab7;\n}\n.panel-primary > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #337ab7;\n}\n.panel-primary > .panel-heading .badge {\n color: #337ab7;\n background-color: #fff;\n}\n.panel-primary > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #337ab7;\n}\n.panel-success {\n border-color: #d6e9c6;\n}\n.panel-success > .panel-heading {\n color: #3c763d;\n background-color: #dff0d8;\n border-color: #d6e9c6;\n}\n.panel-success > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #d6e9c6;\n}\n.panel-success > .panel-heading .badge {\n color: #dff0d8;\n background-color: #3c763d;\n}\n.panel-success > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #d6e9c6;\n}\n.panel-info {\n border-color: #bce8f1;\n}\n.panel-info > .panel-heading {\n color: #31708f;\n background-color: #d9edf7;\n border-color: #bce8f1;\n}\n.panel-info > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #bce8f1;\n}\n.panel-info > .panel-heading .badge {\n color: #d9edf7;\n background-color: #31708f;\n}\n.panel-info > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #bce8f1;\n}\n.panel-warning {\n border-color: #faebcc;\n}\n.panel-warning > .panel-heading {\n color: #8a6d3b;\n background-color: #fcf8e3;\n border-color: #faebcc;\n}\n.panel-warning > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #faebcc;\n}\n.panel-warning > .panel-heading .badge {\n color: #fcf8e3;\n background-color: #8a6d3b;\n}\n.panel-warning > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #faebcc;\n}\n.panel-danger {\n border-color: #ebccd1;\n}\n.panel-danger > .panel-heading {\n color: #a94442;\n background-color: #f2dede;\n border-color: #ebccd1;\n}\n.panel-danger > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #ebccd1;\n}\n.panel-danger > .panel-heading .badge {\n color: #f2dede;\n background-color: #a94442;\n}\n.panel-danger > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #ebccd1;\n}\n.embed-responsive {\n position: relative;\n display: block;\n height: 0;\n padding: 0;\n overflow: hidden;\n}\n.embed-responsive .embed-responsive-item,\n.embed-responsive iframe,\n.embed-responsive embed,\n.embed-responsive object,\n.embed-responsive video {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n width: 100%;\n height: 100%;\n border: 0;\n}\n.embed-responsive-16by9 {\n padding-bottom: 56.25%;\n}\n.embed-responsive-4by3 {\n padding-bottom: 75%;\n}\n.well {\n min-height: 20px;\n padding: 19px;\n margin-bottom: 20px;\n background-color: #f5f5f5;\n border: 1px solid #e3e3e3;\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05);\n}\n.well blockquote {\n border-color: #ddd;\n border-color: rgba(0, 0, 0, .15);\n}\n.well-lg {\n padding: 24px;\n border-radius: 6px;\n}\n.well-sm {\n padding: 9px;\n border-radius: 3px;\n}\n.close {\n float: right;\n font-size: 21px;\n font-weight: bold;\n line-height: 1;\n color: #000;\n text-shadow: 0 1px 0 #fff;\n filter: alpha(opacity=20);\n opacity: .2;\n}\n.close:hover,\n.close:focus {\n color: #000;\n text-decoration: none;\n cursor: pointer;\n filter: alpha(opacity=50);\n opacity: .5;\n}\nbutton.close {\n -webkit-appearance: none;\n padding: 0;\n cursor: pointer;\n background: transparent;\n border: 0;\n}\n.modal-open {\n overflow: hidden;\n}\n.modal {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1050;\n display: none;\n overflow: hidden;\n -webkit-overflow-scrolling: touch;\n outline: 0;\n}\n.modal.fade .modal-dialog {\n -webkit-transition: -webkit-transform .3s ease-out;\n -o-transition: -o-transform .3s ease-out;\n transition: transform .3s ease-out;\n -webkit-transform: translate(0, -25%);\n -ms-transform: translate(0, -25%);\n -o-transform: translate(0, -25%);\n transform: translate(0, -25%);\n}\n.modal.in .modal-dialog {\n -webkit-transform: translate(0, 0);\n -ms-transform: translate(0, 0);\n -o-transform: translate(0, 0);\n transform: translate(0, 0);\n}\n.modal-open .modal {\n overflow-x: hidden;\n overflow-y: auto;\n}\n.modal-dialog {\n position: relative;\n width: auto;\n margin: 10px;\n}\n.modal-content {\n position: relative;\n background-color: #fff;\n -webkit-background-clip: padding-box;\n background-clip: padding-box;\n border: 1px solid #999;\n border: 1px solid rgba(0, 0, 0, .2);\n border-radius: 6px;\n outline: 0;\n -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5);\n box-shadow: 0 3px 9px rgba(0, 0, 0, .5);\n}\n.modal-backdrop {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1040;\n background-color: #000;\n}\n.modal-backdrop.fade {\n filter: alpha(opacity=0);\n opacity: 0;\n}\n.modal-backdrop.in {\n filter: alpha(opacity=50);\n opacity: .5;\n}\n.modal-header {\n padding: 15px;\n border-bottom: 1px solid #e5e5e5;\n}\n.modal-header .close {\n margin-top: -2px;\n}\n.modal-title {\n margin: 0;\n line-height: 1.42857143;\n}\n.modal-body {\n position: relative;\n padding: 15px;\n}\n.modal-footer {\n padding: 15px;\n text-align: right;\n border-top: 1px solid #e5e5e5;\n}\n.modal-footer .btn + .btn {\n margin-bottom: 0;\n margin-left: 5px;\n}\n.modal-footer .btn-group .btn + .btn {\n margin-left: -1px;\n}\n.modal-footer .btn-block + .btn-block {\n margin-left: 0;\n}\n.modal-scrollbar-measure {\n position: absolute;\n top: -9999px;\n width: 50px;\n height: 50px;\n overflow: scroll;\n}\n@media (min-width: 768px) {\n .modal-dialog {\n width: 600px;\n margin: 30px auto;\n }\n .modal-content {\n -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5);\n box-shadow: 0 5px 15px rgba(0, 0, 0, .5);\n }\n .modal-sm {\n width: 300px;\n }\n}\n@media (min-width: 992px) {\n .modal-lg {\n width: 900px;\n }\n}\n.tooltip {\n position: absolute;\n z-index: 1070;\n display: block;\n font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n font-size: 12px;\n font-style: normal;\n font-weight: normal;\n line-height: 1.42857143;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n letter-spacing: normal;\n word-break: normal;\n word-spacing: normal;\n word-wrap: normal;\n white-space: normal;\n filter: alpha(opacity=0);\n opacity: 0;\n\n line-break: auto;\n}\n.tooltip.in {\n filter: alpha(opacity=90);\n opacity: .9;\n}\n.tooltip.top {\n padding: 5px 0;\n margin-top: -3px;\n}\n.tooltip.right {\n padding: 0 5px;\n margin-left: 3px;\n}\n.tooltip.bottom {\n padding: 5px 0;\n margin-top: 3px;\n}\n.tooltip.left {\n padding: 0 5px;\n margin-left: -3px;\n}\n.tooltip-inner {\n max-width: 200px;\n padding: 3px 8px;\n color: #fff;\n text-align: center;\n background-color: #000;\n border-radius: 4px;\n}\n.tooltip-arrow {\n position: absolute;\n width: 0;\n height: 0;\n border-color: transparent;\n border-style: solid;\n}\n.tooltip.top .tooltip-arrow {\n bottom: 0;\n left: 50%;\n margin-left: -5px;\n border-width: 5px 5px 0;\n border-top-color: #000;\n}\n.tooltip.top-left .tooltip-arrow {\n right: 5px;\n bottom: 0;\n margin-bottom: -5px;\n border-width: 5px 5px 0;\n border-top-color: #000;\n}\n.tooltip.top-right .tooltip-arrow {\n bottom: 0;\n left: 5px;\n margin-bottom: -5px;\n border-width: 5px 5px 0;\n border-top-color: #000;\n}\n.tooltip.right .tooltip-arrow {\n top: 50%;\n left: 0;\n margin-top: -5px;\n border-width: 5px 5px 5px 0;\n border-right-color: #000;\n}\n.tooltip.left .tooltip-arrow {\n top: 50%;\n right: 0;\n margin-top: -5px;\n border-width: 5px 0 5px 5px;\n border-left-color: #000;\n}\n.tooltip.bottom .tooltip-arrow {\n top: 0;\n left: 50%;\n margin-left: -5px;\n border-width: 0 5px 5px;\n border-bottom-color: #000;\n}\n.tooltip.bottom-left .tooltip-arrow {\n top: 0;\n right: 5px;\n margin-top: -5px;\n border-width: 0 5px 5px;\n border-bottom-color: #000;\n}\n.tooltip.bottom-right .tooltip-arrow {\n top: 0;\n left: 5px;\n margin-top: -5px;\n border-width: 0 5px 5px;\n border-bottom-color: #000;\n}\n.popover {\n position: absolute;\n top: 0;\n left: 0;\n z-index: 1060;\n display: none;\n max-width: 276px;\n padding: 1px;\n font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n font-size: 14px;\n font-style: normal;\n font-weight: normal;\n line-height: 1.42857143;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n letter-spacing: normal;\n word-break: normal;\n word-spacing: normal;\n word-wrap: normal;\n white-space: normal;\n background-color: #fff;\n -webkit-background-clip: padding-box;\n background-clip: padding-box;\n border: 1px solid #ccc;\n border: 1px solid rgba(0, 0, 0, .2);\n border-radius: 6px;\n -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2);\n box-shadow: 0 5px 10px rgba(0, 0, 0, .2);\n\n line-break: auto;\n}\n.popover.top {\n margin-top: -10px;\n}\n.popover.right {\n margin-left: 10px;\n}\n.popover.bottom {\n margin-top: 10px;\n}\n.popover.left {\n margin-left: -10px;\n}\n.popover-title {\n padding: 8px 14px;\n margin: 0;\n font-size: 14px;\n background-color: #f7f7f7;\n border-bottom: 1px solid #ebebeb;\n border-radius: 5px 5px 0 0;\n}\n.popover-content {\n padding: 9px 14px;\n}\n.popover > .arrow,\n.popover > .arrow:after {\n position: absolute;\n display: block;\n width: 0;\n height: 0;\n border-color: transparent;\n border-style: solid;\n}\n.popover > .arrow {\n border-width: 11px;\n}\n.popover > .arrow:after {\n content: \"\";\n border-width: 10px;\n}\n.popover.top > .arrow {\n bottom: -11px;\n left: 50%;\n margin-left: -11px;\n border-top-color: #999;\n border-top-color: rgba(0, 0, 0, .25);\n border-bottom-width: 0;\n}\n.popover.top > .arrow:after {\n bottom: 1px;\n margin-left: -10px;\n content: \" \";\n border-top-color: #fff;\n border-bottom-width: 0;\n}\n.popover.right > .arrow {\n top: 50%;\n left: -11px;\n margin-top: -11px;\n border-right-color: #999;\n border-right-color: rgba(0, 0, 0, .25);\n border-left-width: 0;\n}\n.popover.right > .arrow:after {\n bottom: -10px;\n left: 1px;\n content: \" \";\n border-right-color: #fff;\n border-left-width: 0;\n}\n.popover.bottom > .arrow {\n top: -11px;\n left: 50%;\n margin-left: -11px;\n border-top-width: 0;\n border-bottom-color: #999;\n border-bottom-color: rgba(0, 0, 0, .25);\n}\n.popover.bottom > .arrow:after {\n top: 1px;\n margin-left: -10px;\n content: \" \";\n border-top-width: 0;\n border-bottom-color: #fff;\n}\n.popover.left > .arrow {\n top: 50%;\n right: -11px;\n margin-top: -11px;\n border-right-width: 0;\n border-left-color: #999;\n border-left-color: rgba(0, 0, 0, .25);\n}\n.popover.left > .arrow:after {\n right: 1px;\n bottom: -10px;\n content: \" \";\n border-right-width: 0;\n border-left-color: #fff;\n}\n.carousel {\n position: relative;\n}\n.carousel-inner {\n position: relative;\n width: 100%;\n overflow: hidden;\n}\n.carousel-inner > .item {\n position: relative;\n display: none;\n -webkit-transition: .6s ease-in-out left;\n -o-transition: .6s ease-in-out left;\n transition: .6s ease-in-out left;\n}\n.carousel-inner > .item > img,\n.carousel-inner > .item > a > img {\n line-height: 1;\n}\n@media all and (transform-3d), (-webkit-transform-3d) {\n .carousel-inner > .item {\n -webkit-transition: -webkit-transform .6s ease-in-out;\n -o-transition: -o-transform .6s ease-in-out;\n transition: transform .6s ease-in-out;\n\n -webkit-backface-visibility: hidden;\n backface-visibility: hidden;\n -webkit-perspective: 1000px;\n perspective: 1000px;\n }\n .carousel-inner > .item.next,\n .carousel-inner > .item.active.right {\n left: 0;\n -webkit-transform: translate3d(100%, 0, 0);\n transform: translate3d(100%, 0, 0);\n }\n .carousel-inner > .item.prev,\n .carousel-inner > .item.active.left {\n left: 0;\n -webkit-transform: translate3d(-100%, 0, 0);\n transform: translate3d(-100%, 0, 0);\n }\n .carousel-inner > .item.next.left,\n .carousel-inner > .item.prev.right,\n .carousel-inner > .item.active {\n left: 0;\n -webkit-transform: translate3d(0, 0, 0);\n transform: translate3d(0, 0, 0);\n }\n}\n.carousel-inner > .active,\n.carousel-inner > .next,\n.carousel-inner > .prev {\n display: block;\n}\n.carousel-inner > .active {\n left: 0;\n}\n.carousel-inner > .next,\n.carousel-inner > .prev {\n position: absolute;\n top: 0;\n width: 100%;\n}\n.carousel-inner > .next {\n left: 100%;\n}\n.carousel-inner > .prev {\n left: -100%;\n}\n.carousel-inner > .next.left,\n.carousel-inner > .prev.right {\n left: 0;\n}\n.carousel-inner > .active.left {\n left: -100%;\n}\n.carousel-inner > .active.right {\n left: 100%;\n}\n.carousel-control {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n width: 15%;\n font-size: 20px;\n color: #fff;\n text-align: center;\n text-shadow: 0 1px 2px rgba(0, 0, 0, .6);\n background-color: rgba(0, 0, 0, 0);\n filter: alpha(opacity=50);\n opacity: .5;\n}\n.carousel-control.left {\n background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%);\n background-image: -o-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%);\n background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .5)), to(rgba(0, 0, 0, .0001)));\n background-image: linear-gradient(to right, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);\n background-repeat: repeat-x;\n}\n.carousel-control.right {\n right: 0;\n left: auto;\n background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%);\n background-image: -o-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%);\n background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .0001)), to(rgba(0, 0, 0, .5)));\n background-image: linear-gradient(to right, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);\n background-repeat: repeat-x;\n}\n.carousel-control:hover,\n.carousel-control:focus {\n color: #fff;\n text-decoration: none;\n filter: alpha(opacity=90);\n outline: 0;\n opacity: .9;\n}\n.carousel-control .icon-prev,\n.carousel-control .icon-next,\n.carousel-control .glyphicon-chevron-left,\n.carousel-control .glyphicon-chevron-right {\n position: absolute;\n top: 50%;\n z-index: 5;\n display: inline-block;\n margin-top: -10px;\n}\n.carousel-control .icon-prev,\n.carousel-control .glyphicon-chevron-left {\n left: 50%;\n margin-left: -10px;\n}\n.carousel-control .icon-next,\n.carousel-control .glyphicon-chevron-right {\n right: 50%;\n margin-right: -10px;\n}\n.carousel-control .icon-prev,\n.carousel-control .icon-next {\n width: 20px;\n height: 20px;\n font-family: serif;\n line-height: 1;\n}\n.carousel-control .icon-prev:before {\n content: '\\2039';\n}\n.carousel-control .icon-next:before {\n content: '\\203a';\n}\n.carousel-indicators {\n position: absolute;\n bottom: 10px;\n left: 50%;\n z-index: 15;\n width: 60%;\n padding-left: 0;\n margin-left: -30%;\n text-align: center;\n list-style: none;\n}\n.carousel-indicators li {\n display: inline-block;\n width: 10px;\n height: 10px;\n margin: 1px;\n text-indent: -999px;\n cursor: pointer;\n background-color: #000 \\9;\n background-color: rgba(0, 0, 0, 0);\n border: 1px solid #fff;\n border-radius: 10px;\n}\n.carousel-indicators .active {\n width: 12px;\n height: 12px;\n margin: 0;\n background-color: #fff;\n}\n.carousel-caption {\n position: absolute;\n right: 15%;\n bottom: 20px;\n left: 15%;\n z-index: 10;\n padding-top: 20px;\n padding-bottom: 20px;\n color: #fff;\n text-align: center;\n text-shadow: 0 1px 2px rgba(0, 0, 0, .6);\n}\n.carousel-caption .btn {\n text-shadow: none;\n}\n@media screen and (min-width: 768px) {\n .carousel-control .glyphicon-chevron-left,\n .carousel-control .glyphicon-chevron-right,\n .carousel-control .icon-prev,\n .carousel-control .icon-next {\n width: 30px;\n height: 30px;\n margin-top: -10px;\n font-size: 30px;\n }\n .carousel-control .glyphicon-chevron-left,\n .carousel-control .icon-prev {\n margin-left: -10px;\n }\n .carousel-control .glyphicon-chevron-right,\n .carousel-control .icon-next {\n margin-right: -10px;\n }\n .carousel-caption {\n right: 20%;\n left: 20%;\n padding-bottom: 30px;\n }\n .carousel-indicators {\n bottom: 20px;\n }\n}\n.clearfix:before,\n.clearfix:after,\n.dl-horizontal dd:before,\n.dl-horizontal dd:after,\n.container:before,\n.container:after,\n.container-fluid:before,\n.container-fluid:after,\n.row:before,\n.row:after,\n.form-horizontal .form-group:before,\n.form-horizontal .form-group:after,\n.btn-toolbar:before,\n.btn-toolbar:after,\n.btn-group-vertical > .btn-group:before,\n.btn-group-vertical > .btn-group:after,\n.nav:before,\n.nav:after,\n.navbar:before,\n.navbar:after,\n.navbar-header:before,\n.navbar-header:after,\n.navbar-collapse:before,\n.navbar-collapse:after,\n.pager:before,\n.pager:after,\n.panel-body:before,\n.panel-body:after,\n.modal-header:before,\n.modal-header:after,\n.modal-footer:before,\n.modal-footer:after {\n display: table;\n content: \" \";\n}\n.clearfix:after,\n.dl-horizontal dd:after,\n.container:after,\n.container-fluid:after,\n.row:after,\n.form-horizontal .form-group:after,\n.btn-toolbar:after,\n.btn-group-vertical > .btn-group:after,\n.nav:after,\n.navbar:after,\n.navbar-header:after,\n.navbar-collapse:after,\n.pager:after,\n.panel-body:after,\n.modal-header:after,\n.modal-footer:after {\n clear: both;\n}\n.center-block {\n display: block;\n margin-right: auto;\n margin-left: auto;\n}\n.pull-right {\n float: right !important;\n}\n.pull-left {\n float: left !important;\n}\n.hide {\n display: none !important;\n}\n.show {\n display: block !important;\n}\n.invisible {\n visibility: hidden;\n}\n.text-hide {\n font: 0/0 a;\n color: transparent;\n text-shadow: none;\n background-color: transparent;\n border: 0;\n}\n.hidden {\n display: none !important;\n}\n.affix {\n position: fixed;\n}\n@-ms-viewport {\n width: device-width;\n}\n.visible-xs,\n.visible-sm,\n.visible-md,\n.visible-lg {\n display: none !important;\n}\n.visible-xs-block,\n.visible-xs-inline,\n.visible-xs-inline-block,\n.visible-sm-block,\n.visible-sm-inline,\n.visible-sm-inline-block,\n.visible-md-block,\n.visible-md-inline,\n.visible-md-inline-block,\n.visible-lg-block,\n.visible-lg-inline,\n.visible-lg-inline-block {\n display: none !important;\n}\n@media (max-width: 767px) {\n .visible-xs {\n display: block !important;\n }\n table.visible-xs {\n display: table !important;\n }\n tr.visible-xs {\n display: table-row !important;\n }\n th.visible-xs,\n td.visible-xs {\n display: table-cell !important;\n }\n}\n@media (max-width: 767px) {\n .visible-xs-block {\n display: block !important;\n }\n}\n@media (max-width: 767px) {\n .visible-xs-inline {\n display: inline !important;\n }\n}\n@media (max-width: 767px) {\n .visible-xs-inline-block {\n display: inline-block !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm {\n display: block !important;\n }\n table.visible-sm {\n display: table !important;\n }\n tr.visible-sm {\n display: table-row !important;\n }\n th.visible-sm,\n td.visible-sm {\n display: table-cell !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm-block {\n display: block !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm-inline {\n display: inline !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm-inline-block {\n display: inline-block !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md {\n display: block !important;\n }\n table.visible-md {\n display: table !important;\n }\n tr.visible-md {\n display: table-row !important;\n }\n th.visible-md,\n td.visible-md {\n display: table-cell !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md-block {\n display: block !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md-inline {\n display: inline !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md-inline-block {\n display: inline-block !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg {\n display: block !important;\n }\n table.visible-lg {\n display: table !important;\n }\n tr.visible-lg {\n display: table-row !important;\n }\n th.visible-lg,\n td.visible-lg {\n display: table-cell !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg-block {\n display: block !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg-inline {\n display: inline !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg-inline-block {\n display: inline-block !important;\n }\n}\n@media (max-width: 767px) {\n .hidden-xs {\n display: none !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .hidden-sm {\n display: none !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .hidden-md {\n display: none !important;\n }\n}\n@media (min-width: 1200px) {\n .hidden-lg {\n display: none !important;\n }\n}\n.visible-print {\n display: none !important;\n}\n@media print {\n .visible-print {\n display: block !important;\n }\n table.visible-print {\n display: table !important;\n }\n tr.visible-print {\n display: table-row !important;\n }\n th.visible-print,\n td.visible-print {\n display: table-cell !important;\n }\n}\n.visible-print-block {\n display: none !important;\n}\n@media print {\n .visible-print-block {\n display: block !important;\n }\n}\n.visible-print-inline {\n display: none !important;\n}\n@media print {\n .visible-print-inline {\n display: inline !important;\n }\n}\n.visible-print-inline-block {\n display: none !important;\n}\n@media print {\n .visible-print-inline-block {\n display: inline-block !important;\n }\n}\n@media print {\n .hidden-print {\n display: none !important;\n }\n}\n/*# sourceMappingURL=bootstrap.css.map */\n","//\n// Glyphicons for Bootstrap\n//\n// Since icons are fonts, they can be placed anywhere text is placed and are\n// thus automatically sized to match the surrounding child. To use, create an\n// inline element with the appropriate classes, like so:\n//\n// Star\n\n// Import the fonts\n@font-face {\n font-family: 'Glyphicons Halflings';\n src: url('@{icon-font-path}@{icon-font-name}.eot');\n src: url('@{icon-font-path}@{icon-font-name}.eot?#iefix') format('embedded-opentype'),\n url('@{icon-font-path}@{icon-font-name}.woff2') format('woff2'),\n url('@{icon-font-path}@{icon-font-name}.woff') format('woff'),\n url('@{icon-font-path}@{icon-font-name}.ttf') format('truetype'),\n url('@{icon-font-path}@{icon-font-name}.svg#@{icon-font-svg-id}') format('svg');\n}\n\n// Catchall baseclass\n.glyphicon {\n position: relative;\n top: 1px;\n display: inline-block;\n font-family: 'Glyphicons Halflings';\n font-style: normal;\n font-weight: normal;\n line-height: 1;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n// Individual icons\n.glyphicon-asterisk { &:before { content: \"\\002a\"; } }\n.glyphicon-plus { &:before { content: \"\\002b\"; } }\n.glyphicon-euro,\n.glyphicon-eur { &:before { content: \"\\20ac\"; } }\n.glyphicon-minus { &:before { content: \"\\2212\"; } }\n.glyphicon-cloud { &:before { content: \"\\2601\"; } }\n.glyphicon-envelope { &:before { content: \"\\2709\"; } }\n.glyphicon-pencil { &:before { content: \"\\270f\"; } }\n.glyphicon-glass { &:before { content: \"\\e001\"; } }\n.glyphicon-music { &:before { content: \"\\e002\"; } }\n.glyphicon-search { &:before { content: \"\\e003\"; } }\n.glyphicon-heart { &:before { content: \"\\e005\"; } }\n.glyphicon-star { &:before { content: \"\\e006\"; } }\n.glyphicon-star-empty { &:before { content: \"\\e007\"; } }\n.glyphicon-user { &:before { content: \"\\e008\"; } }\n.glyphicon-film { &:before { content: \"\\e009\"; } }\n.glyphicon-th-large { &:before { content: \"\\e010\"; } }\n.glyphicon-th { &:before { content: \"\\e011\"; } }\n.glyphicon-th-list { &:before { content: \"\\e012\"; } }\n.glyphicon-ok { &:before { content: \"\\e013\"; } }\n.glyphicon-remove { &:before { content: \"\\e014\"; } }\n.glyphicon-zoom-in { &:before { content: \"\\e015\"; } }\n.glyphicon-zoom-out { &:before { content: \"\\e016\"; } }\n.glyphicon-off { &:before { content: \"\\e017\"; } }\n.glyphicon-signal { &:before { content: \"\\e018\"; } }\n.glyphicon-cog { &:before { content: \"\\e019\"; } }\n.glyphicon-trash { &:before { content: \"\\e020\"; } }\n.glyphicon-home { &:before { content: \"\\e021\"; } }\n.glyphicon-file { &:before { content: \"\\e022\"; } }\n.glyphicon-time { &:before { content: \"\\e023\"; } }\n.glyphicon-road { &:before { content: \"\\e024\"; } }\n.glyphicon-download-alt { &:before { content: \"\\e025\"; } }\n.glyphicon-download { &:before { content: \"\\e026\"; } }\n.glyphicon-upload { &:before { content: \"\\e027\"; } }\n.glyphicon-inbox { &:before { content: \"\\e028\"; } }\n.glyphicon-play-circle { &:before { content: \"\\e029\"; } }\n.glyphicon-repeat { &:before { content: \"\\e030\"; } }\n.glyphicon-refresh { &:before { content: \"\\e031\"; } }\n.glyphicon-list-alt { &:before { content: \"\\e032\"; } }\n.glyphicon-lock { &:before { content: \"\\e033\"; } }\n.glyphicon-flag { &:before { content: \"\\e034\"; } }\n.glyphicon-headphones { &:before { content: \"\\e035\"; } }\n.glyphicon-volume-off { &:before { content: \"\\e036\"; } }\n.glyphicon-volume-down { &:before { content: \"\\e037\"; } }\n.glyphicon-volume-up { &:before { content: \"\\e038\"; } }\n.glyphicon-qrcode { &:before { content: \"\\e039\"; } }\n.glyphicon-barcode { &:before { content: \"\\e040\"; } }\n.glyphicon-tag { &:before { content: \"\\e041\"; } }\n.glyphicon-tags { &:before { content: \"\\e042\"; } }\n.glyphicon-book { &:before { content: \"\\e043\"; } }\n.glyphicon-bookmark { &:before { content: \"\\e044\"; } }\n.glyphicon-print { &:before { content: \"\\e045\"; } }\n.glyphicon-camera { &:before { content: \"\\e046\"; } }\n.glyphicon-font { &:before { content: \"\\e047\"; } }\n.glyphicon-bold { &:before { content: \"\\e048\"; } }\n.glyphicon-italic { &:before { content: \"\\e049\"; } }\n.glyphicon-text-height { &:before { content: \"\\e050\"; } }\n.glyphicon-text-width { &:before { content: \"\\e051\"; } }\n.glyphicon-align-left { &:before { content: \"\\e052\"; } }\n.glyphicon-align-center { &:before { content: \"\\e053\"; } }\n.glyphicon-align-right { &:before { content: \"\\e054\"; } }\n.glyphicon-align-justify { &:before { content: \"\\e055\"; } }\n.glyphicon-list { &:before { content: \"\\e056\"; } }\n.glyphicon-indent-left { &:before { content: \"\\e057\"; } }\n.glyphicon-indent-right { &:before { content: \"\\e058\"; } }\n.glyphicon-facetime-video { &:before { content: \"\\e059\"; } }\n.glyphicon-picture { &:before { content: \"\\e060\"; } }\n.glyphicon-map-marker { &:before { content: \"\\e062\"; } }\n.glyphicon-adjust { &:before { content: \"\\e063\"; } }\n.glyphicon-tint { &:before { content: \"\\e064\"; } }\n.glyphicon-edit { &:before { content: \"\\e065\"; } }\n.glyphicon-share { &:before { content: \"\\e066\"; } }\n.glyphicon-check { &:before { content: \"\\e067\"; } }\n.glyphicon-move { &:before { content: \"\\e068\"; } }\n.glyphicon-step-backward { &:before { content: \"\\e069\"; } }\n.glyphicon-fast-backward { &:before { content: \"\\e070\"; } }\n.glyphicon-backward { &:before { content: \"\\e071\"; } }\n.glyphicon-play { &:before { content: \"\\e072\"; } }\n.glyphicon-pause { &:before { content: \"\\e073\"; } }\n.glyphicon-stop { &:before { content: \"\\e074\"; } }\n.glyphicon-forward { &:before { content: \"\\e075\"; } }\n.glyphicon-fast-forward { &:before { content: \"\\e076\"; } }\n.glyphicon-step-forward { &:before { content: \"\\e077\"; } }\n.glyphicon-eject { &:before { content: \"\\e078\"; } }\n.glyphicon-chevron-left { &:before { content: \"\\e079\"; } }\n.glyphicon-chevron-right { &:before { content: \"\\e080\"; } }\n.glyphicon-plus-sign { &:before { content: \"\\e081\"; } }\n.glyphicon-minus-sign { &:before { content: \"\\e082\"; } }\n.glyphicon-remove-sign { &:before { content: \"\\e083\"; } }\n.glyphicon-ok-sign { &:before { content: \"\\e084\"; } }\n.glyphicon-question-sign { &:before { content: \"\\e085\"; } }\n.glyphicon-info-sign { &:before { content: \"\\e086\"; } }\n.glyphicon-screenshot { &:before { content: \"\\e087\"; } }\n.glyphicon-remove-circle { &:before { content: \"\\e088\"; } }\n.glyphicon-ok-circle { &:before { content: \"\\e089\"; } }\n.glyphicon-ban-circle { &:before { content: \"\\e090\"; } }\n.glyphicon-arrow-left { &:before { content: \"\\e091\"; } }\n.glyphicon-arrow-right { &:before { content: \"\\e092\"; } }\n.glyphicon-arrow-up { &:before { content: \"\\e093\"; } }\n.glyphicon-arrow-down { &:before { content: \"\\e094\"; } }\n.glyphicon-share-alt { &:before { content: \"\\e095\"; } }\n.glyphicon-resize-full { &:before { content: \"\\e096\"; } }\n.glyphicon-resize-small { &:before { content: \"\\e097\"; } }\n.glyphicon-exclamation-sign { &:before { content: \"\\e101\"; } }\n.glyphicon-gift { &:before { content: \"\\e102\"; } }\n.glyphicon-leaf { &:before { content: \"\\e103\"; } }\n.glyphicon-fire { &:before { content: \"\\e104\"; } }\n.glyphicon-eye-open { &:before { content: \"\\e105\"; } }\n.glyphicon-eye-close { &:before { content: \"\\e106\"; } }\n.glyphicon-warning-sign { &:before { content: \"\\e107\"; } }\n.glyphicon-plane { &:before { content: \"\\e108\"; } }\n.glyphicon-calendar { &:before { content: \"\\e109\"; } }\n.glyphicon-random { &:before { content: \"\\e110\"; } }\n.glyphicon-comment { &:before { content: \"\\e111\"; } }\n.glyphicon-magnet { &:before { content: \"\\e112\"; } }\n.glyphicon-chevron-up { &:before { content: \"\\e113\"; } }\n.glyphicon-chevron-down { &:before { content: \"\\e114\"; } }\n.glyphicon-retweet { &:before { content: \"\\e115\"; } }\n.glyphicon-shopping-cart { &:before { content: \"\\e116\"; } }\n.glyphicon-folder-close { &:before { content: \"\\e117\"; } }\n.glyphicon-folder-open { &:before { content: \"\\e118\"; } }\n.glyphicon-resize-vertical { &:before { content: \"\\e119\"; } }\n.glyphicon-resize-horizontal { &:before { content: \"\\e120\"; } }\n.glyphicon-hdd { &:before { content: \"\\e121\"; } }\n.glyphicon-bullhorn { &:before { content: \"\\e122\"; } }\n.glyphicon-bell { &:before { content: \"\\e123\"; } }\n.glyphicon-certificate { &:before { content: \"\\e124\"; } }\n.glyphicon-thumbs-up { &:before { content: \"\\e125\"; } }\n.glyphicon-thumbs-down { &:before { content: \"\\e126\"; } }\n.glyphicon-hand-right { &:before { content: \"\\e127\"; } }\n.glyphicon-hand-left { &:before { content: \"\\e128\"; } }\n.glyphicon-hand-up { &:before { content: \"\\e129\"; } }\n.glyphicon-hand-down { &:before { content: \"\\e130\"; } }\n.glyphicon-circle-arrow-right { &:before { content: \"\\e131\"; } }\n.glyphicon-circle-arrow-left { &:before { content: \"\\e132\"; } }\n.glyphicon-circle-arrow-up { &:before { content: \"\\e133\"; } }\n.glyphicon-circle-arrow-down { &:before { content: \"\\e134\"; } }\n.glyphicon-globe { &:before { content: \"\\e135\"; } }\n.glyphicon-wrench { &:before { content: \"\\e136\"; } }\n.glyphicon-tasks { &:before { content: \"\\e137\"; } }\n.glyphicon-filter { &:before { content: \"\\e138\"; } }\n.glyphicon-briefcase { &:before { content: \"\\e139\"; } }\n.glyphicon-fullscreen { &:before { content: \"\\e140\"; } }\n.glyphicon-dashboard { &:before { content: \"\\e141\"; } }\n.glyphicon-paperclip { &:before { content: \"\\e142\"; } }\n.glyphicon-heart-empty { &:before { content: \"\\e143\"; } }\n.glyphicon-link { &:before { content: \"\\e144\"; } }\n.glyphicon-phone { &:before { content: \"\\e145\"; } }\n.glyphicon-pushpin { &:before { content: \"\\e146\"; } }\n.glyphicon-usd { &:before { content: \"\\e148\"; } }\n.glyphicon-gbp { &:before { content: \"\\e149\"; } }\n.glyphicon-sort { &:before { content: \"\\e150\"; } }\n.glyphicon-sort-by-alphabet { &:before { content: \"\\e151\"; } }\n.glyphicon-sort-by-alphabet-alt { &:before { content: \"\\e152\"; } }\n.glyphicon-sort-by-order { &:before { content: \"\\e153\"; } }\n.glyphicon-sort-by-order-alt { &:before { content: \"\\e154\"; } }\n.glyphicon-sort-by-attributes { &:before { content: \"\\e155\"; } }\n.glyphicon-sort-by-attributes-alt { &:before { content: \"\\e156\"; } }\n.glyphicon-unchecked { &:before { content: \"\\e157\"; } }\n.glyphicon-expand { &:before { content: \"\\e158\"; } }\n.glyphicon-collapse-down { &:before { content: \"\\e159\"; } }\n.glyphicon-collapse-up { &:before { content: \"\\e160\"; } }\n.glyphicon-log-in { &:before { content: \"\\e161\"; } }\n.glyphicon-flash { &:before { content: \"\\e162\"; } }\n.glyphicon-log-out { &:before { content: \"\\e163\"; } }\n.glyphicon-new-window { &:before { content: \"\\e164\"; } }\n.glyphicon-record { &:before { content: \"\\e165\"; } }\n.glyphicon-save { &:before { content: \"\\e166\"; } }\n.glyphicon-open { &:before { content: \"\\e167\"; } }\n.glyphicon-saved { &:before { content: \"\\e168\"; } }\n.glyphicon-import { &:before { content: \"\\e169\"; } }\n.glyphicon-export { &:before { content: \"\\e170\"; } }\n.glyphicon-send { &:before { content: \"\\e171\"; } }\n.glyphicon-floppy-disk { &:before { content: \"\\e172\"; } }\n.glyphicon-floppy-saved { &:before { content: \"\\e173\"; } }\n.glyphicon-floppy-remove { &:before { content: \"\\e174\"; } }\n.glyphicon-floppy-save { &:before { content: \"\\e175\"; } }\n.glyphicon-floppy-open { &:before { content: \"\\e176\"; } }\n.glyphicon-credit-card { &:before { content: \"\\e177\"; } }\n.glyphicon-transfer { &:before { content: \"\\e178\"; } }\n.glyphicon-cutlery { &:before { content: \"\\e179\"; } }\n.glyphicon-header { &:before { content: \"\\e180\"; } }\n.glyphicon-compressed { &:before { content: \"\\e181\"; } }\n.glyphicon-earphone { &:before { content: \"\\e182\"; } }\n.glyphicon-phone-alt { &:before { content: \"\\e183\"; } }\n.glyphicon-tower { &:before { content: \"\\e184\"; } }\n.glyphicon-stats { &:before { content: \"\\e185\"; } }\n.glyphicon-sd-video { &:before { content: \"\\e186\"; } }\n.glyphicon-hd-video { &:before { content: \"\\e187\"; } }\n.glyphicon-subtitles { &:before { content: \"\\e188\"; } }\n.glyphicon-sound-stereo { &:before { content: \"\\e189\"; } }\n.glyphicon-sound-dolby { &:before { content: \"\\e190\"; } }\n.glyphicon-sound-5-1 { &:before { content: \"\\e191\"; } }\n.glyphicon-sound-6-1 { &:before { content: \"\\e192\"; } }\n.glyphicon-sound-7-1 { &:before { content: \"\\e193\"; } }\n.glyphicon-copyright-mark { &:before { content: \"\\e194\"; } }\n.glyphicon-registration-mark { &:before { content: \"\\e195\"; } }\n.glyphicon-cloud-download { &:before { content: \"\\e197\"; } }\n.glyphicon-cloud-upload { &:before { content: \"\\e198\"; } }\n.glyphicon-tree-conifer { &:before { content: \"\\e199\"; } }\n.glyphicon-tree-deciduous { &:before { content: \"\\e200\"; } }\n.glyphicon-cd { &:before { content: \"\\e201\"; } }\n.glyphicon-save-file { &:before { content: \"\\e202\"; } }\n.glyphicon-open-file { &:before { content: \"\\e203\"; } }\n.glyphicon-level-up { &:before { content: \"\\e204\"; } }\n.glyphicon-copy { &:before { content: \"\\e205\"; } }\n.glyphicon-paste { &:before { content: \"\\e206\"; } }\n// The following 2 Glyphicons are omitted for the time being because\n// they currently use Unicode codepoints that are outside the\n// Basic Multilingual Plane (BMP). Older buggy versions of WebKit can't handle\n// non-BMP codepoints in CSS string escapes, and thus can't display these two icons.\n// Notably, the bug affects some older versions of the Android Browser.\n// More info: https://github.com/twbs/bootstrap/issues/10106\n// .glyphicon-door { &:before { content: \"\\1f6aa\"; } }\n// .glyphicon-key { &:before { content: \"\\1f511\"; } }\n.glyphicon-alert { &:before { content: \"\\e209\"; } }\n.glyphicon-equalizer { &:before { content: \"\\e210\"; } }\n.glyphicon-king { &:before { content: \"\\e211\"; } }\n.glyphicon-queen { &:before { content: \"\\e212\"; } }\n.glyphicon-pawn { &:before { content: \"\\e213\"; } }\n.glyphicon-bishop { &:before { content: \"\\e214\"; } }\n.glyphicon-knight { &:before { content: \"\\e215\"; } }\n.glyphicon-baby-formula { &:before { content: \"\\e216\"; } }\n.glyphicon-tent { &:before { content: \"\\26fa\"; } }\n.glyphicon-blackboard { &:before { content: \"\\e218\"; } }\n.glyphicon-bed { &:before { content: \"\\e219\"; } }\n.glyphicon-apple { &:before { content: \"\\f8ff\"; } }\n.glyphicon-erase { &:before { content: \"\\e221\"; } }\n.glyphicon-hourglass { &:before { content: \"\\231b\"; } }\n.glyphicon-lamp { &:before { content: \"\\e223\"; } }\n.glyphicon-duplicate { &:before { content: \"\\e224\"; } }\n.glyphicon-piggy-bank { &:before { content: \"\\e225\"; } }\n.glyphicon-scissors { &:before { content: \"\\e226\"; } }\n.glyphicon-bitcoin { &:before { content: \"\\e227\"; } }\n.glyphicon-btc { &:before { content: \"\\e227\"; } }\n.glyphicon-xbt { &:before { content: \"\\e227\"; } }\n.glyphicon-yen { &:before { content: \"\\00a5\"; } }\n.glyphicon-jpy { &:before { content: \"\\00a5\"; } }\n.glyphicon-ruble { &:before { content: \"\\20bd\"; } }\n.glyphicon-rub { &:before { content: \"\\20bd\"; } }\n.glyphicon-scale { &:before { content: \"\\e230\"; } }\n.glyphicon-ice-lolly { &:before { content: \"\\e231\"; } }\n.glyphicon-ice-lolly-tasted { &:before { content: \"\\e232\"; } }\n.glyphicon-education { &:before { content: \"\\e233\"; } }\n.glyphicon-option-horizontal { &:before { content: \"\\e234\"; } }\n.glyphicon-option-vertical { &:before { content: \"\\e235\"; } }\n.glyphicon-menu-hamburger { &:before { content: \"\\e236\"; } }\n.glyphicon-modal-window { &:before { content: \"\\e237\"; } }\n.glyphicon-oil { &:before { content: \"\\e238\"; } }\n.glyphicon-grain { &:before { content: \"\\e239\"; } }\n.glyphicon-sunglasses { &:before { content: \"\\e240\"; } }\n.glyphicon-text-size { &:before { content: \"\\e241\"; } }\n.glyphicon-text-color { &:before { content: \"\\e242\"; } }\n.glyphicon-text-background { &:before { content: \"\\e243\"; } }\n.glyphicon-object-align-top { &:before { content: \"\\e244\"; } }\n.glyphicon-object-align-bottom { &:before { content: \"\\e245\"; } }\n.glyphicon-object-align-horizontal{ &:before { content: \"\\e246\"; } }\n.glyphicon-object-align-left { &:before { content: \"\\e247\"; } }\n.glyphicon-object-align-vertical { &:before { content: \"\\e248\"; } }\n.glyphicon-object-align-right { &:before { content: \"\\e249\"; } }\n.glyphicon-triangle-right { &:before { content: \"\\e250\"; } }\n.glyphicon-triangle-left { &:before { content: \"\\e251\"; } }\n.glyphicon-triangle-bottom { &:before { content: \"\\e252\"; } }\n.glyphicon-triangle-top { &:before { content: \"\\e253\"; } }\n.glyphicon-console { &:before { content: \"\\e254\"; } }\n.glyphicon-superscript { &:before { content: \"\\e255\"; } }\n.glyphicon-subscript { &:before { content: \"\\e256\"; } }\n.glyphicon-menu-left { &:before { content: \"\\e257\"; } }\n.glyphicon-menu-right { &:before { content: \"\\e258\"; } }\n.glyphicon-menu-down { &:before { content: \"\\e259\"; } }\n.glyphicon-menu-up { &:before { content: \"\\e260\"; } }\n","//\n// Scaffolding\n// --------------------------------------------------\n\n\n// Reset the box-sizing\n//\n// Heads up! This reset may cause conflicts with some third-party widgets.\n// For recommendations on resolving such conflicts, see\n// http://getbootstrap.com/getting-started/#third-box-sizing\n* {\n .box-sizing(border-box);\n}\n*:before,\n*:after {\n .box-sizing(border-box);\n}\n\n\n// Body reset\n\nhtml {\n font-size: 10px;\n -webkit-tap-highlight-color: rgba(0,0,0,0);\n}\n\nbody {\n font-family: @font-family-base;\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @text-color;\n background-color: @body-bg;\n}\n\n// Reset fonts for relevant elements\ninput,\nbutton,\nselect,\ntextarea {\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\n\n// Links\n\na {\n color: @link-color;\n text-decoration: none;\n\n &:hover,\n &:focus {\n color: @link-hover-color;\n text-decoration: @link-hover-decoration;\n }\n\n &:focus {\n .tab-focus();\n }\n}\n\n\n// Figures\n//\n// We reset this here because previously Normalize had no `figure` margins. This\n// ensures we don't break anyone's use of the element.\n\nfigure {\n margin: 0;\n}\n\n\n// Images\n\nimg {\n vertical-align: middle;\n}\n\n// Responsive images (ensure images don't scale beyond their parents)\n.img-responsive {\n .img-responsive();\n}\n\n// Rounded corners\n.img-rounded {\n border-radius: @border-radius-large;\n}\n\n// Image thumbnails\n//\n// Heads up! This is mixin-ed into thumbnails.less for `.thumbnail`.\n.img-thumbnail {\n padding: @thumbnail-padding;\n line-height: @line-height-base;\n background-color: @thumbnail-bg;\n border: 1px solid @thumbnail-border;\n border-radius: @thumbnail-border-radius;\n .transition(all .2s ease-in-out);\n\n // Keep them at most 100% wide\n .img-responsive(inline-block);\n}\n\n// Perfect circle\n.img-circle {\n border-radius: 50%; // set radius in percents\n}\n\n\n// Horizontal rules\n\nhr {\n margin-top: @line-height-computed;\n margin-bottom: @line-height-computed;\n border: 0;\n border-top: 1px solid @hr-border;\n}\n\n\n// Only display content to screen readers\n//\n// See: http://a11yproject.com/posts/how-to-hide-content\n\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0,0,0,0);\n border: 0;\n}\n\n// Use in conjunction with .sr-only to only display content when it's focused.\n// Useful for \"Skip to main content\" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1\n// Credit: HTML5 Boilerplate\n\n.sr-only-focusable {\n &:active,\n &:focus {\n position: static;\n width: auto;\n height: auto;\n margin: 0;\n overflow: visible;\n clip: auto;\n }\n}\n\n\n// iOS \"clickable elements\" fix for role=\"button\"\n//\n// Fixes \"clickability\" issue (and more generally, the firing of events such as focus as well)\n// for traditionally non-focusable elements with role=\"button\"\n// see https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile\n\n[role=\"button\"] {\n cursor: pointer;\n}\n","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They have been removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility) {\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n word-wrap: break-word;\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n // Firefox\n &::-moz-placeholder {\n color: @color;\n opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526\n }\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n","// WebKit-style focus\n\n.tab-focus() {\n // WebKit-specific. Other browsers will keep their default outline style.\n // (Initially tried to also force default via `outline: initial`,\n // but that seems to erroneously remove the outline in Firefox altogether.)\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\n","// Image Mixins\n// - Responsive image\n// - Retina image\n\n\n// Responsive image\n//\n// Keep images from scaling beyond the width of their parents.\n.img-responsive(@display: block) {\n display: @display;\n max-width: 100%; // Part 1: Set a maximum relative to the parent\n height: auto; // Part 2: Scale the height according to the width, otherwise you get stretching\n}\n\n\n// Retina image\n//\n// Short retina mixin for setting background-image and -size. Note that the\n// spelling of `min--moz-device-pixel-ratio` is intentional.\n.img-retina(@file-1x; @file-2x; @width-1x; @height-1x) {\n background-image: url(\"@{file-1x}\");\n\n @media\n only screen and (-webkit-min-device-pixel-ratio: 2),\n only screen and ( min--moz-device-pixel-ratio: 2),\n only screen and ( -o-min-device-pixel-ratio: 2/1),\n only screen and ( min-device-pixel-ratio: 2),\n only screen and ( min-resolution: 192dpi),\n only screen and ( min-resolution: 2dppx) {\n background-image: url(\"@{file-2x}\");\n background-size: @width-1x @height-1x;\n }\n}\n","//\n// Typography\n// --------------------------------------------------\n\n\n// Headings\n// -------------------------\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n font-family: @headings-font-family;\n font-weight: @headings-font-weight;\n line-height: @headings-line-height;\n color: @headings-color;\n\n small,\n .small {\n font-weight: normal;\n line-height: 1;\n color: @headings-small-color;\n }\n}\n\nh1, .h1,\nh2, .h2,\nh3, .h3 {\n margin-top: @line-height-computed;\n margin-bottom: (@line-height-computed / 2);\n\n small,\n .small {\n font-size: 65%;\n }\n}\nh4, .h4,\nh5, .h5,\nh6, .h6 {\n margin-top: (@line-height-computed / 2);\n margin-bottom: (@line-height-computed / 2);\n\n small,\n .small {\n font-size: 75%;\n }\n}\n\nh1, .h1 { font-size: @font-size-h1; }\nh2, .h2 { font-size: @font-size-h2; }\nh3, .h3 { font-size: @font-size-h3; }\nh4, .h4 { font-size: @font-size-h4; }\nh5, .h5 { font-size: @font-size-h5; }\nh6, .h6 { font-size: @font-size-h6; }\n\n\n// Body text\n// -------------------------\n\np {\n margin: 0 0 (@line-height-computed / 2);\n}\n\n.lead {\n margin-bottom: @line-height-computed;\n font-size: floor((@font-size-base * 1.15));\n font-weight: 300;\n line-height: 1.4;\n\n @media (min-width: @screen-sm-min) {\n font-size: (@font-size-base * 1.5);\n }\n}\n\n\n// Emphasis & misc\n// -------------------------\n\n// Ex: (12px small font / 14px base font) * 100% = about 85%\nsmall,\n.small {\n font-size: floor((100% * @font-size-small / @font-size-base));\n}\n\nmark,\n.mark {\n background-color: @state-warning-bg;\n padding: .2em;\n}\n\n// Alignment\n.text-left { text-align: left; }\n.text-right { text-align: right; }\n.text-center { text-align: center; }\n.text-justify { text-align: justify; }\n.text-nowrap { white-space: nowrap; }\n\n// Transformation\n.text-lowercase { text-transform: lowercase; }\n.text-uppercase { text-transform: uppercase; }\n.text-capitalize { text-transform: capitalize; }\n\n// Contextual colors\n.text-muted {\n color: @text-muted;\n}\n.text-primary {\n .text-emphasis-variant(@brand-primary);\n}\n.text-success {\n .text-emphasis-variant(@state-success-text);\n}\n.text-info {\n .text-emphasis-variant(@state-info-text);\n}\n.text-warning {\n .text-emphasis-variant(@state-warning-text);\n}\n.text-danger {\n .text-emphasis-variant(@state-danger-text);\n}\n\n// Contextual backgrounds\n// For now we'll leave these alongside the text classes until v4 when we can\n// safely shift things around (per SemVer rules).\n.bg-primary {\n // Given the contrast here, this is the only class to have its color inverted\n // automatically.\n color: #fff;\n .bg-variant(@brand-primary);\n}\n.bg-success {\n .bg-variant(@state-success-bg);\n}\n.bg-info {\n .bg-variant(@state-info-bg);\n}\n.bg-warning {\n .bg-variant(@state-warning-bg);\n}\n.bg-danger {\n .bg-variant(@state-danger-bg);\n}\n\n\n// Page header\n// -------------------------\n\n.page-header {\n padding-bottom: ((@line-height-computed / 2) - 1);\n margin: (@line-height-computed * 2) 0 @line-height-computed;\n border-bottom: 1px solid @page-header-border-color;\n}\n\n\n// Lists\n// -------------------------\n\n// Unordered and Ordered lists\nul,\nol {\n margin-top: 0;\n margin-bottom: (@line-height-computed / 2);\n ul,\n ol {\n margin-bottom: 0;\n }\n}\n\n// List options\n\n// Unstyled keeps list items block level, just removes default browser padding and list-style\n.list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n\n// Inline turns list items into inline-block\n.list-inline {\n .list-unstyled();\n margin-left: -5px;\n\n > li {\n display: inline-block;\n padding-left: 5px;\n padding-right: 5px;\n }\n}\n\n// Description Lists\ndl {\n margin-top: 0; // Remove browser default\n margin-bottom: @line-height-computed;\n}\ndt,\ndd {\n line-height: @line-height-base;\n}\ndt {\n font-weight: bold;\n}\ndd {\n margin-left: 0; // Undo browser default\n}\n\n// Horizontal description lists\n//\n// Defaults to being stacked without any of the below styles applied, until the\n// grid breakpoint is reached (default of ~768px).\n\n.dl-horizontal {\n dd {\n &:extend(.clearfix all); // Clear the floated `dt` if an empty `dd` is present\n }\n\n @media (min-width: @dl-horizontal-breakpoint) {\n dt {\n float: left;\n width: (@dl-horizontal-offset - 20);\n clear: left;\n text-align: right;\n .text-overflow();\n }\n dd {\n margin-left: @dl-horizontal-offset;\n }\n }\n}\n\n\n// Misc\n// -------------------------\n\n// Abbreviations and acronyms\nabbr[title],\n// Add data-* attribute to help out our tooltip plugin, per https://github.com/twbs/bootstrap/issues/5257\nabbr[data-original-title] {\n cursor: help;\n border-bottom: 1px dotted @abbr-border-color;\n}\n.initialism {\n font-size: 90%;\n .text-uppercase();\n}\n\n// Blockquotes\nblockquote {\n padding: (@line-height-computed / 2) @line-height-computed;\n margin: 0 0 @line-height-computed;\n font-size: @blockquote-font-size;\n border-left: 5px solid @blockquote-border-color;\n\n p,\n ul,\n ol {\n &:last-child {\n margin-bottom: 0;\n }\n }\n\n // Note: Deprecated small and .small as of v3.1.0\n // Context: https://github.com/twbs/bootstrap/issues/11660\n footer,\n small,\n .small {\n display: block;\n font-size: 80%; // back to default font-size\n line-height: @line-height-base;\n color: @blockquote-small-color;\n\n &:before {\n content: '\\2014 \\00A0'; // em dash, nbsp\n }\n }\n}\n\n// Opposite alignment of blockquote\n//\n// Heads up: `blockquote.pull-right` has been deprecated as of v3.1.0.\n.blockquote-reverse,\nblockquote.pull-right {\n padding-right: 15px;\n padding-left: 0;\n border-right: 5px solid @blockquote-border-color;\n border-left: 0;\n text-align: right;\n\n // Account for citation\n footer,\n small,\n .small {\n &:before { content: ''; }\n &:after {\n content: '\\00A0 \\2014'; // nbsp, em dash\n }\n }\n}\n\n// Addresses\naddress {\n margin-bottom: @line-height-computed;\n font-style: normal;\n line-height: @line-height-base;\n}\n","// Typography\n\n.text-emphasis-variant(@color) {\n color: @color;\n a&:hover,\n a&:focus {\n color: darken(@color, 10%);\n }\n}\n","// Contextual backgrounds\n\n.bg-variant(@color) {\n background-color: @color;\n a&:hover,\n a&:focus {\n background-color: darken(@color, 10%);\n }\n}\n","// Text overflow\n// Requires inline-block or block for proper styling\n\n.text-overflow() {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n","//\n// Code (inline and block)\n// --------------------------------------------------\n\n\n// Inline and block code styles\ncode,\nkbd,\npre,\nsamp {\n font-family: @font-family-monospace;\n}\n\n// Inline code\ncode {\n padding: 2px 4px;\n font-size: 90%;\n color: @code-color;\n background-color: @code-bg;\n border-radius: @border-radius-base;\n}\n\n// User input typically entered via keyboard\nkbd {\n padding: 2px 4px;\n font-size: 90%;\n color: @kbd-color;\n background-color: @kbd-bg;\n border-radius: @border-radius-small;\n box-shadow: inset 0 -1px 0 rgba(0,0,0,.25);\n\n kbd {\n padding: 0;\n font-size: 100%;\n font-weight: bold;\n box-shadow: none;\n }\n}\n\n// Blocks of code\npre {\n display: block;\n padding: ((@line-height-computed - 1) / 2);\n margin: 0 0 (@line-height-computed / 2);\n font-size: (@font-size-base - 1); // 14px to 13px\n line-height: @line-height-base;\n word-break: break-all;\n word-wrap: break-word;\n color: @pre-color;\n background-color: @pre-bg;\n border: 1px solid @pre-border-color;\n border-radius: @border-radius-base;\n\n // Account for some code outputs that place code tags in pre tags\n code {\n padding: 0;\n font-size: inherit;\n color: inherit;\n white-space: pre-wrap;\n background-color: transparent;\n border-radius: 0;\n }\n}\n\n// Enable scrollable blocks of code\n.pre-scrollable {\n max-height: @pre-scrollable-max-height;\n overflow-y: scroll;\n}\n","//\n// Grid system\n// --------------------------------------------------\n\n\n// Container widths\n//\n// Set the container width, and override it for fixed navbars in media queries.\n\n.container {\n .container-fixed();\n\n @media (min-width: @screen-sm-min) {\n width: @container-sm;\n }\n @media (min-width: @screen-md-min) {\n width: @container-md;\n }\n @media (min-width: @screen-lg-min) {\n width: @container-lg;\n }\n}\n\n\n// Fluid container\n//\n// Utilizes the mixin meant for fixed width containers, but without any defined\n// width for fluid, full width layouts.\n\n.container-fluid {\n .container-fixed();\n}\n\n\n// Row\n//\n// Rows contain and clear the floats of your columns.\n\n.row {\n .make-row();\n}\n\n\n// Columns\n//\n// Common styles for small and large grid columns\n\n.make-grid-columns();\n\n\n// Extra small grid\n//\n// Columns, offsets, pushes, and pulls for extra small devices like\n// smartphones.\n\n.make-grid(xs);\n\n\n// Small grid\n//\n// Columns, offsets, pushes, and pulls for the small device range, from phones\n// to tablets.\n\n@media (min-width: @screen-sm-min) {\n .make-grid(sm);\n}\n\n\n// Medium grid\n//\n// Columns, offsets, pushes, and pulls for the desktop device range.\n\n@media (min-width: @screen-md-min) {\n .make-grid(md);\n}\n\n\n// Large grid\n//\n// Columns, offsets, pushes, and pulls for the large desktop device range.\n\n@media (min-width: @screen-lg-min) {\n .make-grid(lg);\n}\n","// Grid system\n//\n// Generate semantic grid columns with these mixins.\n\n// Centered container element\n.container-fixed(@gutter: @grid-gutter-width) {\n margin-right: auto;\n margin-left: auto;\n padding-left: floor((@gutter / 2));\n padding-right: ceil((@gutter / 2));\n &:extend(.clearfix all);\n}\n\n// Creates a wrapper for a series of columns\n.make-row(@gutter: @grid-gutter-width) {\n margin-left: ceil((@gutter / -2));\n margin-right: floor((@gutter / -2));\n &:extend(.clearfix all);\n}\n\n// Generate the extra small columns\n.make-xs-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n float: left;\n width: percentage((@columns / @grid-columns));\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n}\n.make-xs-column-offset(@columns) {\n margin-left: percentage((@columns / @grid-columns));\n}\n.make-xs-column-push(@columns) {\n left: percentage((@columns / @grid-columns));\n}\n.make-xs-column-pull(@columns) {\n right: percentage((@columns / @grid-columns));\n}\n\n// Generate the small columns\n.make-sm-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-sm-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-offset(@columns) {\n @media (min-width: @screen-sm-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-push(@columns) {\n @media (min-width: @screen-sm-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-pull(@columns) {\n @media (min-width: @screen-sm-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n\n// Generate the medium columns\n.make-md-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-md-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-offset(@columns) {\n @media (min-width: @screen-md-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-push(@columns) {\n @media (min-width: @screen-md-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-pull(@columns) {\n @media (min-width: @screen-md-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n\n// Generate the large columns\n.make-lg-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-lg-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-offset(@columns) {\n @media (min-width: @screen-lg-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-push(@columns) {\n @media (min-width: @screen-lg-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-pull(@columns) {\n @media (min-width: @screen-lg-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n","// Framework grid generation\n//\n// Used only by Bootstrap to generate the correct number of grid classes given\n// any value of `@grid-columns`.\n\n.make-grid-columns() {\n // Common styles for all sizes of grid columns, widths 1-12\n .col(@index) { // initial\n @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n .col((@index + 1), @item);\n }\n .col(@index, @list) when (@index =< @grid-columns) { // general; \"=<\" isn't a typo\n @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n .col((@index + 1), ~\"@{list}, @{item}\");\n }\n .col(@index, @list) when (@index > @grid-columns) { // terminal\n @{list} {\n position: relative;\n // Prevent columns from collapsing when empty\n min-height: 1px;\n // Inner gutter via padding\n padding-left: ceil((@grid-gutter-width / 2));\n padding-right: floor((@grid-gutter-width / 2));\n }\n }\n .col(1); // kickstart it\n}\n\n.float-grid-columns(@class) {\n .col(@index) { // initial\n @item: ~\".col-@{class}-@{index}\";\n .col((@index + 1), @item);\n }\n .col(@index, @list) when (@index =< @grid-columns) { // general\n @item: ~\".col-@{class}-@{index}\";\n .col((@index + 1), ~\"@{list}, @{item}\");\n }\n .col(@index, @list) when (@index > @grid-columns) { // terminal\n @{list} {\n float: left;\n }\n }\n .col(1); // kickstart it\n}\n\n.calc-grid-column(@index, @class, @type) when (@type = width) and (@index > 0) {\n .col-@{class}-@{index} {\n width: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = push) and (@index > 0) {\n .col-@{class}-push-@{index} {\n left: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = push) and (@index = 0) {\n .col-@{class}-push-0 {\n left: auto;\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index > 0) {\n .col-@{class}-pull-@{index} {\n right: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index = 0) {\n .col-@{class}-pull-0 {\n right: auto;\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = offset) {\n .col-@{class}-offset-@{index} {\n margin-left: percentage((@index / @grid-columns));\n }\n}\n\n// Basic looping in LESS\n.loop-grid-columns(@index, @class, @type) when (@index >= 0) {\n .calc-grid-column(@index, @class, @type);\n // next iteration\n .loop-grid-columns((@index - 1), @class, @type);\n}\n\n// Create grid for specific class\n.make-grid(@class) {\n .float-grid-columns(@class);\n .loop-grid-columns(@grid-columns, @class, width);\n .loop-grid-columns(@grid-columns, @class, pull);\n .loop-grid-columns(@grid-columns, @class, push);\n .loop-grid-columns(@grid-columns, @class, offset);\n}\n","//\n// Tables\n// --------------------------------------------------\n\n\ntable {\n background-color: @table-bg;\n}\ncaption {\n padding-top: @table-cell-padding;\n padding-bottom: @table-cell-padding;\n color: @text-muted;\n text-align: left;\n}\nth {\n text-align: left;\n}\n\n\n// Baseline styles\n\n.table {\n width: 100%;\n max-width: 100%;\n margin-bottom: @line-height-computed;\n // Cells\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n padding: @table-cell-padding;\n line-height: @line-height-base;\n vertical-align: top;\n border-top: 1px solid @table-border-color;\n }\n }\n }\n // Bottom align for column headings\n > thead > tr > th {\n vertical-align: bottom;\n border-bottom: 2px solid @table-border-color;\n }\n // Remove top border from thead by default\n > caption + thead,\n > colgroup + thead,\n > thead:first-child {\n > tr:first-child {\n > th,\n > td {\n border-top: 0;\n }\n }\n }\n // Account for multiple tbody instances\n > tbody + tbody {\n border-top: 2px solid @table-border-color;\n }\n\n // Nesting\n .table {\n background-color: @body-bg;\n }\n}\n\n\n// Condensed table w/ half padding\n\n.table-condensed {\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n padding: @table-condensed-cell-padding;\n }\n }\n }\n}\n\n\n// Bordered version\n//\n// Add borders all around the table and between all the columns.\n\n.table-bordered {\n border: 1px solid @table-border-color;\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n border: 1px solid @table-border-color;\n }\n }\n }\n > thead > tr {\n > th,\n > td {\n border-bottom-width: 2px;\n }\n }\n}\n\n\n// Zebra-striping\n//\n// Default zebra-stripe styles (alternating gray and transparent backgrounds)\n\n.table-striped {\n > tbody > tr:nth-of-type(odd) {\n background-color: @table-bg-accent;\n }\n}\n\n\n// Hover effect\n//\n// Placed here since it has to come after the potential zebra striping\n\n.table-hover {\n > tbody > tr:hover {\n background-color: @table-bg-hover;\n }\n}\n\n\n// Table cell sizing\n//\n// Reset default table behavior\n\ntable col[class*=\"col-\"] {\n position: static; // Prevent border hiding in Firefox and IE9-11 (see https://github.com/twbs/bootstrap/issues/11623)\n float: none;\n display: table-column;\n}\ntable {\n td,\n th {\n &[class*=\"col-\"] {\n position: static; // Prevent border hiding in Firefox and IE9-11 (see https://github.com/twbs/bootstrap/issues/11623)\n float: none;\n display: table-cell;\n }\n }\n}\n\n\n// Table backgrounds\n//\n// Exact selectors below required to override `.table-striped` and prevent\n// inheritance to nested tables.\n\n// Generate the contextual variants\n.table-row-variant(active; @table-bg-active);\n.table-row-variant(success; @state-success-bg);\n.table-row-variant(info; @state-info-bg);\n.table-row-variant(warning; @state-warning-bg);\n.table-row-variant(danger; @state-danger-bg);\n\n\n// Responsive tables\n//\n// Wrap your tables in `.table-responsive` and we'll make them mobile friendly\n// by enabling horizontal scrolling. Only applies <768px. Everything above that\n// will display normally.\n\n.table-responsive {\n overflow-x: auto;\n min-height: 0.01%; // Workaround for IE9 bug (see https://github.com/twbs/bootstrap/issues/14837)\n\n @media screen and (max-width: @screen-xs-max) {\n width: 100%;\n margin-bottom: (@line-height-computed * 0.75);\n overflow-y: hidden;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n border: 1px solid @table-border-color;\n\n // Tighten up spacing\n > .table {\n margin-bottom: 0;\n\n // Ensure the content doesn't wrap\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n white-space: nowrap;\n }\n }\n }\n }\n\n // Special overrides for the bordered tables\n > .table-bordered {\n border: 0;\n\n // Nuke the appropriate borders so that the parent can handle them\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th:first-child,\n > td:first-child {\n border-left: 0;\n }\n > th:last-child,\n > td:last-child {\n border-right: 0;\n }\n }\n }\n\n // Only nuke the last row's bottom-border in `tbody` and `tfoot` since\n // chances are there will be only one `tr` in a `thead` and that would\n // remove the border altogether.\n > tbody,\n > tfoot {\n > tr:last-child {\n > th,\n > td {\n border-bottom: 0;\n }\n }\n }\n\n }\n }\n}\n","// Tables\n\n.table-row-variant(@state; @background) {\n // Exact selectors below required to override `.table-striped` and prevent\n // inheritance to nested tables.\n .table > thead > tr,\n .table > tbody > tr,\n .table > tfoot > tr {\n > td.@{state},\n > th.@{state},\n &.@{state} > td,\n &.@{state} > th {\n background-color: @background;\n }\n }\n\n // Hover states for `.table-hover`\n // Note: this is not available for cells or rows within `thead` or `tfoot`.\n .table-hover > tbody > tr {\n > td.@{state}:hover,\n > th.@{state}:hover,\n &.@{state}:hover > td,\n &:hover > .@{state},\n &.@{state}:hover > th {\n background-color: darken(@background, 5%);\n }\n }\n}\n","//\n// Forms\n// --------------------------------------------------\n\n\n// Normalize non-controls\n//\n// Restyle and baseline non-control form elements.\n\nfieldset {\n padding: 0;\n margin: 0;\n border: 0;\n // Chrome and Firefox set a `min-width: min-content;` on fieldsets,\n // so we reset that to ensure it behaves more like a standard block element.\n // See https://github.com/twbs/bootstrap/issues/12359.\n min-width: 0;\n}\n\nlegend {\n display: block;\n width: 100%;\n padding: 0;\n margin-bottom: @line-height-computed;\n font-size: (@font-size-base * 1.5);\n line-height: inherit;\n color: @legend-color;\n border: 0;\n border-bottom: 1px solid @legend-border-color;\n}\n\nlabel {\n display: inline-block;\n max-width: 100%; // Force IE8 to wrap long content (see https://github.com/twbs/bootstrap/issues/13141)\n margin-bottom: 5px;\n font-weight: bold;\n}\n\n\n// Normalize form controls\n//\n// While most of our form styles require extra classes, some basic normalization\n// is required to ensure optimum display with or without those classes to better\n// address browser inconsistencies.\n\n// Override content-box in Normalize (* isn't specific enough)\ninput[type=\"search\"] {\n .box-sizing(border-box);\n}\n\n// Position radios and checkboxes better\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n margin: 4px 0 0;\n margin-top: 1px \\9; // IE8-9\n line-height: normal;\n}\n\ninput[type=\"file\"] {\n display: block;\n}\n\n// Make range inputs behave like textual form controls\ninput[type=\"range\"] {\n display: block;\n width: 100%;\n}\n\n// Make multiple select elements height not fixed\nselect[multiple],\nselect[size] {\n height: auto;\n}\n\n// Focus for file, radio, and checkbox\ninput[type=\"file\"]:focus,\ninput[type=\"radio\"]:focus,\ninput[type=\"checkbox\"]:focus {\n .tab-focus();\n}\n\n// Adjust output element\noutput {\n display: block;\n padding-top: (@padding-base-vertical + 1);\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @input-color;\n}\n\n\n// Common form controls\n//\n// Shared size and type resets for form controls. Apply `.form-control` to any\n// of the following form controls:\n//\n// select\n// textarea\n// input[type=\"text\"]\n// input[type=\"password\"]\n// input[type=\"datetime\"]\n// input[type=\"datetime-local\"]\n// input[type=\"date\"]\n// input[type=\"month\"]\n// input[type=\"time\"]\n// input[type=\"week\"]\n// input[type=\"number\"]\n// input[type=\"email\"]\n// input[type=\"url\"]\n// input[type=\"search\"]\n// input[type=\"tel\"]\n// input[type=\"color\"]\n\n.form-control {\n display: block;\n width: 100%;\n height: @input-height-base; // Make inputs at least the height of their button counterpart (base line-height + padding + border)\n padding: @padding-base-vertical @padding-base-horizontal;\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @input-color;\n background-color: @input-bg;\n background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n border: 1px solid @input-border;\n border-radius: @input-border-radius; // Note: This has no effect on s in CSS.\n .box-shadow(inset 0 1px 1px rgba(0,0,0,.075));\n .transition(~\"border-color ease-in-out .15s, box-shadow ease-in-out .15s\");\n\n // Customize the `:focus` state to imitate native WebKit styles.\n .form-control-focus();\n\n // Placeholder\n .placeholder();\n\n // Unstyle the caret on ``\n// element gets special love because it's special, and that's a fact!\n.input-size(@input-height; @padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {\n height: @input-height;\n padding: @padding-vertical @padding-horizontal;\n font-size: @font-size;\n line-height: @line-height;\n border-radius: @border-radius;\n\n select& {\n height: @input-height;\n line-height: @input-height;\n }\n\n textarea&,\n select[multiple]& {\n height: auto;\n }\n}\n","//\n// Buttons\n// --------------------------------------------------\n\n\n// Base styles\n// --------------------------------------------------\n\n.btn {\n display: inline-block;\n margin-bottom: 0; // For input.btn\n font-weight: @btn-font-weight;\n text-align: center;\n vertical-align: middle;\n touch-action: manipulation;\n cursor: pointer;\n background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n border: 1px solid transparent;\n white-space: nowrap;\n .button-size(@padding-base-vertical; @padding-base-horizontal; @font-size-base; @line-height-base; @btn-border-radius-base);\n .user-select(none);\n\n &,\n &:active,\n &.active {\n &:focus,\n &.focus {\n .tab-focus();\n }\n }\n\n &:hover,\n &:focus,\n &.focus {\n color: @btn-default-color;\n text-decoration: none;\n }\n\n &:active,\n &.active {\n outline: 0;\n background-image: none;\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n cursor: @cursor-disabled;\n .opacity(.65);\n .box-shadow(none);\n }\n\n a& {\n &.disabled,\n fieldset[disabled] & {\n pointer-events: none; // Future-proof disabling of clicks on `` elements\n }\n }\n}\n\n\n// Alternate buttons\n// --------------------------------------------------\n\n.btn-default {\n .button-variant(@btn-default-color; @btn-default-bg; @btn-default-border);\n}\n.btn-primary {\n .button-variant(@btn-primary-color; @btn-primary-bg; @btn-primary-border);\n}\n// Success appears as green\n.btn-success {\n .button-variant(@btn-success-color; @btn-success-bg; @btn-success-border);\n}\n// Info appears as blue-green\n.btn-info {\n .button-variant(@btn-info-color; @btn-info-bg; @btn-info-border);\n}\n// Warning appears as orange\n.btn-warning {\n .button-variant(@btn-warning-color; @btn-warning-bg; @btn-warning-border);\n}\n// Danger and error appear as red\n.btn-danger {\n .button-variant(@btn-danger-color; @btn-danger-bg; @btn-danger-border);\n}\n\n\n// Link buttons\n// -------------------------\n\n// Make a button look and behave like a link\n.btn-link {\n color: @link-color;\n font-weight: normal;\n border-radius: 0;\n\n &,\n &:active,\n &.active,\n &[disabled],\n fieldset[disabled] & {\n background-color: transparent;\n .box-shadow(none);\n }\n &,\n &:hover,\n &:focus,\n &:active {\n border-color: transparent;\n }\n &:hover,\n &:focus {\n color: @link-hover-color;\n text-decoration: @link-hover-decoration;\n background-color: transparent;\n }\n &[disabled],\n fieldset[disabled] & {\n &:hover,\n &:focus {\n color: @btn-link-disabled-color;\n text-decoration: none;\n }\n }\n}\n\n\n// Button Sizes\n// --------------------------------------------------\n\n.btn-lg {\n // line-height: ensure even-numbered height of button next to large input\n .button-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @btn-border-radius-large);\n}\n.btn-sm {\n // line-height: ensure proper height of button next to small input\n .button-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @btn-border-radius-small);\n}\n.btn-xs {\n .button-size(@padding-xs-vertical; @padding-xs-horizontal; @font-size-small; @line-height-small; @btn-border-radius-small);\n}\n\n\n// Block button\n// --------------------------------------------------\n\n.btn-block {\n display: block;\n width: 100%;\n}\n\n// Vertically space out multiple block buttons\n.btn-block + .btn-block {\n margin-top: 5px;\n}\n\n// Specificity overrides\ninput[type=\"submit\"],\ninput[type=\"reset\"],\ninput[type=\"button\"] {\n &.btn-block {\n width: 100%;\n }\n}\n","// Button variants\n//\n// Easily pump out default styles, as well as :hover, :focus, :active,\n// and disabled options for all buttons\n\n.button-variant(@color; @background; @border) {\n color: @color;\n background-color: @background;\n border-color: @border;\n\n &:focus,\n &.focus {\n color: @color;\n background-color: darken(@background, 10%);\n border-color: darken(@border, 25%);\n }\n &:hover {\n color: @color;\n background-color: darken(@background, 10%);\n border-color: darken(@border, 12%);\n }\n &:active,\n &.active,\n .open > .dropdown-toggle& {\n color: @color;\n background-color: darken(@background, 10%);\n border-color: darken(@border, 12%);\n\n &:hover,\n &:focus,\n &.focus {\n color: @color;\n background-color: darken(@background, 17%);\n border-color: darken(@border, 25%);\n }\n }\n &:active,\n &.active,\n .open > .dropdown-toggle& {\n background-image: none;\n }\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n &:hover,\n &:focus,\n &.focus {\n background-color: @background;\n border-color: @border;\n }\n }\n\n .badge {\n color: @background;\n background-color: @color;\n }\n}\n\n// Button sizes\n.button-size(@padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {\n padding: @padding-vertical @padding-horizontal;\n font-size: @font-size;\n line-height: @line-height;\n border-radius: @border-radius;\n}\n","// Opacity\n\n.opacity(@opacity) {\n opacity: @opacity;\n // IE8 filter\n @opacity-ie: (@opacity * 100);\n filter: ~\"alpha(opacity=@{opacity-ie})\";\n}\n","//\n// Component animations\n// --------------------------------------------------\n\n// Heads up!\n//\n// We don't use the `.opacity()` mixin here since it causes a bug with text\n// fields in IE7-8. Source: https://github.com/twbs/bootstrap/pull/3552.\n\n.fade {\n opacity: 0;\n .transition(opacity .15s linear);\n &.in {\n opacity: 1;\n }\n}\n\n.collapse {\n display: none;\n\n &.in { display: block; }\n tr&.in { display: table-row; }\n tbody&.in { display: table-row-group; }\n}\n\n.collapsing {\n position: relative;\n height: 0;\n overflow: hidden;\n .transition-property(~\"height, visibility\");\n .transition-duration(.35s);\n .transition-timing-function(ease);\n}\n","//\n// Dropdown menus\n// --------------------------------------------------\n\n\n// Dropdown arrow/caret\n.caret {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 2px;\n vertical-align: middle;\n border-top: @caret-width-base dashed;\n border-top: @caret-width-base solid ~\"\\9\"; // IE8\n border-right: @caret-width-base solid transparent;\n border-left: @caret-width-base solid transparent;\n}\n\n// The dropdown wrapper (div)\n.dropup,\n.dropdown {\n position: relative;\n}\n\n// Prevent the focus on the dropdown toggle when closing dropdowns\n.dropdown-toggle:focus {\n outline: 0;\n}\n\n// The dropdown menu (ul)\n.dropdown-menu {\n position: absolute;\n top: 100%;\n left: 0;\n z-index: @zindex-dropdown;\n display: none; // none by default, but block on \"open\" of the menu\n float: left;\n min-width: 160px;\n padding: 5px 0;\n margin: 2px 0 0; // override default ul\n list-style: none;\n font-size: @font-size-base;\n text-align: left; // Ensures proper alignment if parent has it changed (e.g., modal footer)\n background-color: @dropdown-bg;\n border: 1px solid @dropdown-fallback-border; // IE8 fallback\n border: 1px solid @dropdown-border;\n border-radius: @border-radius-base;\n .box-shadow(0 6px 12px rgba(0,0,0,.175));\n background-clip: padding-box;\n\n // Aligns the dropdown menu to right\n //\n // Deprecated as of 3.1.0 in favor of `.dropdown-menu-[dir]`\n &.pull-right {\n right: 0;\n left: auto;\n }\n\n // Dividers (basically an hr) within the dropdown\n .divider {\n .nav-divider(@dropdown-divider-bg);\n }\n\n // Links within the dropdown menu\n > li > a {\n display: block;\n padding: 3px 20px;\n clear: both;\n font-weight: normal;\n line-height: @line-height-base;\n color: @dropdown-link-color;\n white-space: nowrap; // prevent links from randomly breaking onto new lines\n }\n}\n\n// Hover/Focus state\n.dropdown-menu > li > a {\n &:hover,\n &:focus {\n text-decoration: none;\n color: @dropdown-link-hover-color;\n background-color: @dropdown-link-hover-bg;\n }\n}\n\n// Active state\n.dropdown-menu > .active > a {\n &,\n &:hover,\n &:focus {\n color: @dropdown-link-active-color;\n text-decoration: none;\n outline: 0;\n background-color: @dropdown-link-active-bg;\n }\n}\n\n// Disabled state\n//\n// Gray out text and ensure the hover/focus state remains gray\n\n.dropdown-menu > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @dropdown-link-disabled-color;\n }\n\n // Nuke hover/focus effects\n &:hover,\n &:focus {\n text-decoration: none;\n background-color: transparent;\n background-image: none; // Remove CSS gradient\n .reset-filter();\n cursor: @cursor-disabled;\n }\n}\n\n// Open state for the dropdown\n.open {\n // Show the menu\n > .dropdown-menu {\n display: block;\n }\n\n // Remove the outline when :focus is triggered\n > a {\n outline: 0;\n }\n}\n\n// Menu positioning\n//\n// Add extra class to `.dropdown-menu` to flip the alignment of the dropdown\n// menu with the parent.\n.dropdown-menu-right {\n left: auto; // Reset the default from `.dropdown-menu`\n right: 0;\n}\n// With v3, we enabled auto-flipping if you have a dropdown within a right\n// aligned nav component. To enable the undoing of that, we provide an override\n// to restore the default dropdown menu alignment.\n//\n// This is only for left-aligning a dropdown menu within a `.navbar-right` or\n// `.pull-right` nav component.\n.dropdown-menu-left {\n left: 0;\n right: auto;\n}\n\n// Dropdown section headers\n.dropdown-header {\n display: block;\n padding: 3px 20px;\n font-size: @font-size-small;\n line-height: @line-height-base;\n color: @dropdown-header-color;\n white-space: nowrap; // as with > li > a\n}\n\n// Backdrop to catch body clicks on mobile, etc.\n.dropdown-backdrop {\n position: fixed;\n left: 0;\n right: 0;\n bottom: 0;\n top: 0;\n z-index: (@zindex-dropdown - 10);\n}\n\n// Right aligned dropdowns\n.pull-right > .dropdown-menu {\n right: 0;\n left: auto;\n}\n\n// Allow for dropdowns to go bottom up (aka, dropup-menu)\n//\n// Just add .dropup after the standard .dropdown class and you're set, bro.\n// TODO: abstract this so that the navbar fixed styles are not placed here?\n\n.dropup,\n.navbar-fixed-bottom .dropdown {\n // Reverse the caret\n .caret {\n border-top: 0;\n border-bottom: @caret-width-base dashed;\n border-bottom: @caret-width-base solid ~\"\\9\"; // IE8\n content: \"\";\n }\n // Different positioning for bottom up menu\n .dropdown-menu {\n top: auto;\n bottom: 100%;\n margin-bottom: 2px;\n }\n}\n\n\n// Component alignment\n//\n// Reiterate per navbar.less and the modified component alignment there.\n\n@media (min-width: @grid-float-breakpoint) {\n .navbar-right {\n .dropdown-menu {\n .dropdown-menu-right();\n }\n // Necessary for overrides of the default right aligned menu.\n // Will remove come v4 in all likelihood.\n .dropdown-menu-left {\n .dropdown-menu-left();\n }\n }\n}\n","// Horizontal dividers\n//\n// Dividers (basically an hr) within dropdowns and nav lists\n\n.nav-divider(@color: #e5e5e5) {\n height: 1px;\n margin: ((@line-height-computed / 2) - 1) 0;\n overflow: hidden;\n background-color: @color;\n}\n","// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n","//\n// Button groups\n// --------------------------------------------------\n\n// Make the div behave like a button\n.btn-group,\n.btn-group-vertical {\n position: relative;\n display: inline-block;\n vertical-align: middle; // match .btn alignment given font-size hack above\n > .btn {\n position: relative;\n float: left;\n // Bring the \"active\" button to the front\n &:hover,\n &:focus,\n &:active,\n &.active {\n z-index: 2;\n }\n }\n}\n\n// Prevent double borders when buttons are next to each other\n.btn-group {\n .btn + .btn,\n .btn + .btn-group,\n .btn-group + .btn,\n .btn-group + .btn-group {\n margin-left: -1px;\n }\n}\n\n// Optional: Group multiple button groups together for a toolbar\n.btn-toolbar {\n margin-left: -5px; // Offset the first child's margin\n &:extend(.clearfix all);\n\n .btn,\n .btn-group,\n .input-group {\n float: left;\n }\n > .btn,\n > .btn-group,\n > .input-group {\n margin-left: 5px;\n }\n}\n\n.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {\n border-radius: 0;\n}\n\n// Set corners individual because sometimes a single button can be in a .btn-group and we need :first-child and :last-child to both match\n.btn-group > .btn:first-child {\n margin-left: 0;\n &:not(:last-child):not(.dropdown-toggle) {\n .border-right-radius(0);\n }\n}\n// Need .dropdown-toggle since :last-child doesn't apply, given that a .dropdown-menu is used immediately after it\n.btn-group > .btn:last-child:not(:first-child),\n.btn-group > .dropdown-toggle:not(:first-child) {\n .border-left-radius(0);\n}\n\n// Custom edits for including btn-groups within btn-groups (useful for including dropdown buttons within a btn-group)\n.btn-group > .btn-group {\n float: left;\n}\n.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {\n border-radius: 0;\n}\n.btn-group > .btn-group:first-child:not(:last-child) {\n > .btn:last-child,\n > .dropdown-toggle {\n .border-right-radius(0);\n }\n}\n.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child {\n .border-left-radius(0);\n}\n\n// On active and open, don't show outline\n.btn-group .dropdown-toggle:active,\n.btn-group.open .dropdown-toggle {\n outline: 0;\n}\n\n\n// Sizing\n//\n// Remix the default button sizing classes into new ones for easier manipulation.\n\n.btn-group-xs > .btn { &:extend(.btn-xs); }\n.btn-group-sm > .btn { &:extend(.btn-sm); }\n.btn-group-lg > .btn { &:extend(.btn-lg); }\n\n\n// Split button dropdowns\n// ----------------------\n\n// Give the line between buttons some depth\n.btn-group > .btn + .dropdown-toggle {\n padding-left: 8px;\n padding-right: 8px;\n}\n.btn-group > .btn-lg + .dropdown-toggle {\n padding-left: 12px;\n padding-right: 12px;\n}\n\n// The clickable button for toggling the menu\n// Remove the gradient and set the same inset shadow as the :active state\n.btn-group.open .dropdown-toggle {\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n\n // Show no shadow for `.btn-link` since it has no other button styles.\n &.btn-link {\n .box-shadow(none);\n }\n}\n\n\n// Reposition the caret\n.btn .caret {\n margin-left: 0;\n}\n// Carets in other button sizes\n.btn-lg .caret {\n border-width: @caret-width-large @caret-width-large 0;\n border-bottom-width: 0;\n}\n// Upside down carets for .dropup\n.dropup .btn-lg .caret {\n border-width: 0 @caret-width-large @caret-width-large;\n}\n\n\n// Vertical button groups\n// ----------------------\n\n.btn-group-vertical {\n > .btn,\n > .btn-group,\n > .btn-group > .btn {\n display: block;\n float: none;\n width: 100%;\n max-width: 100%;\n }\n\n // Clear floats so dropdown menus can be properly placed\n > .btn-group {\n &:extend(.clearfix all);\n > .btn {\n float: none;\n }\n }\n\n > .btn + .btn,\n > .btn + .btn-group,\n > .btn-group + .btn,\n > .btn-group + .btn-group {\n margin-top: -1px;\n margin-left: 0;\n }\n}\n\n.btn-group-vertical > .btn {\n &:not(:first-child):not(:last-child) {\n border-radius: 0;\n }\n &:first-child:not(:last-child) {\n .border-top-radius(@btn-border-radius-base);\n .border-bottom-radius(0);\n }\n &:last-child:not(:first-child) {\n .border-top-radius(0);\n .border-bottom-radius(@btn-border-radius-base);\n }\n}\n.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {\n border-radius: 0;\n}\n.btn-group-vertical > .btn-group:first-child:not(:last-child) {\n > .btn:last-child,\n > .dropdown-toggle {\n .border-bottom-radius(0);\n }\n}\n.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {\n .border-top-radius(0);\n}\n\n\n// Justified button groups\n// ----------------------\n\n.btn-group-justified {\n display: table;\n width: 100%;\n table-layout: fixed;\n border-collapse: separate;\n > .btn,\n > .btn-group {\n float: none;\n display: table-cell;\n width: 1%;\n }\n > .btn-group .btn {\n width: 100%;\n }\n\n > .btn-group .dropdown-menu {\n left: auto;\n }\n}\n\n\n// Checkbox and radio options\n//\n// In order to support the browser's form validation feedback, powered by the\n// `required` attribute, we have to \"hide\" the inputs via `clip`. We cannot use\n// `display: none;` or `visibility: hidden;` as that also hides the popover.\n// Simply visually hiding the inputs via `opacity` would leave them clickable in\n// certain cases which is prevented by using `clip` and `pointer-events`.\n// This way, we ensure a DOM element is visible to position the popover from.\n//\n// See https://github.com/twbs/bootstrap/pull/12794 and\n// https://github.com/twbs/bootstrap/pull/14559 for more information.\n\n[data-toggle=\"buttons\"] {\n > .btn,\n > .btn-group > .btn {\n input[type=\"radio\"],\n input[type=\"checkbox\"] {\n position: absolute;\n clip: rect(0,0,0,0);\n pointer-events: none;\n }\n }\n}\n","// Single side border-radius\n\n.border-top-radius(@radius) {\n border-top-right-radius: @radius;\n border-top-left-radius: @radius;\n}\n.border-right-radius(@radius) {\n border-bottom-right-radius: @radius;\n border-top-right-radius: @radius;\n}\n.border-bottom-radius(@radius) {\n border-bottom-right-radius: @radius;\n border-bottom-left-radius: @radius;\n}\n.border-left-radius(@radius) {\n border-bottom-left-radius: @radius;\n border-top-left-radius: @radius;\n}\n","//\n// Input groups\n// --------------------------------------------------\n\n// Base styles\n// -------------------------\n.input-group {\n position: relative; // For dropdowns\n display: table;\n border-collapse: separate; // prevent input groups from inheriting border styles from table cells when placed within a table\n\n // Undo padding and float of grid classes\n &[class*=\"col-\"] {\n float: none;\n padding-left: 0;\n padding-right: 0;\n }\n\n .form-control {\n // Ensure that the input is always above the *appended* addon button for\n // proper border colors.\n position: relative;\n z-index: 2;\n\n // IE9 fubars the placeholder attribute in text inputs and the arrows on\n // select elements in input groups. To fix it, we float the input. Details:\n // https://github.com/twbs/bootstrap/issues/11561#issuecomment-28936855\n float: left;\n\n width: 100%;\n margin-bottom: 0;\n\n &:focus {\n z-index: 3;\n }\n }\n}\n\n// Sizing options\n//\n// Remix the default form control sizing classes into new ones for easier\n// manipulation.\n\n.input-group-lg > .form-control,\n.input-group-lg > .input-group-addon,\n.input-group-lg > .input-group-btn > .btn {\n .input-lg();\n}\n.input-group-sm > .form-control,\n.input-group-sm > .input-group-addon,\n.input-group-sm > .input-group-btn > .btn {\n .input-sm();\n}\n\n\n// Display as table-cell\n// -------------------------\n.input-group-addon,\n.input-group-btn,\n.input-group .form-control {\n display: table-cell;\n\n &:not(:first-child):not(:last-child) {\n border-radius: 0;\n }\n}\n// Addon and addon wrapper for buttons\n.input-group-addon,\n.input-group-btn {\n width: 1%;\n white-space: nowrap;\n vertical-align: middle; // Match the inputs\n}\n\n// Text input groups\n// -------------------------\n.input-group-addon {\n padding: @padding-base-vertical @padding-base-horizontal;\n font-size: @font-size-base;\n font-weight: normal;\n line-height: 1;\n color: @input-color;\n text-align: center;\n background-color: @input-group-addon-bg;\n border: 1px solid @input-group-addon-border-color;\n border-radius: @input-border-radius;\n\n // Sizing\n &.input-sm {\n padding: @padding-small-vertical @padding-small-horizontal;\n font-size: @font-size-small;\n border-radius: @input-border-radius-small;\n }\n &.input-lg {\n padding: @padding-large-vertical @padding-large-horizontal;\n font-size: @font-size-large;\n border-radius: @input-border-radius-large;\n }\n\n // Nuke default margins from checkboxes and radios to vertically center within.\n input[type=\"radio\"],\n input[type=\"checkbox\"] {\n margin-top: 0;\n }\n}\n\n// Reset rounded corners\n.input-group .form-control:first-child,\n.input-group-addon:first-child,\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group > .btn,\n.input-group-btn:first-child > .dropdown-toggle,\n.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {\n .border-right-radius(0);\n}\n.input-group-addon:first-child {\n border-right: 0;\n}\n.input-group .form-control:last-child,\n.input-group-addon:last-child,\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group > .btn,\n.input-group-btn:last-child > .dropdown-toggle,\n.input-group-btn:first-child > .btn:not(:first-child),\n.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {\n .border-left-radius(0);\n}\n.input-group-addon:last-child {\n border-left: 0;\n}\n\n// Button input groups\n// -------------------------\n.input-group-btn {\n position: relative;\n // Jankily prevent input button groups from wrapping with `white-space` and\n // `font-size` in combination with `inline-block` on buttons.\n font-size: 0;\n white-space: nowrap;\n\n // Negative margin for spacing, position for bringing hovered/focused/actived\n // element above the siblings.\n > .btn {\n position: relative;\n + .btn {\n margin-left: -1px;\n }\n // Bring the \"active\" button to the front\n &:hover,\n &:focus,\n &:active {\n z-index: 2;\n }\n }\n\n // Negative margin to only have a 1px border between the two\n &:first-child {\n > .btn,\n > .btn-group {\n margin-right: -1px;\n }\n }\n &:last-child {\n > .btn,\n > .btn-group {\n z-index: 2;\n margin-left: -1px;\n }\n }\n}\n","//\n// Navs\n// --------------------------------------------------\n\n\n// Base class\n// --------------------------------------------------\n\n.nav {\n margin-bottom: 0;\n padding-left: 0; // Override default ul/ol\n list-style: none;\n &:extend(.clearfix all);\n\n > li {\n position: relative;\n display: block;\n\n > a {\n position: relative;\n display: block;\n padding: @nav-link-padding;\n &:hover,\n &:focus {\n text-decoration: none;\n background-color: @nav-link-hover-bg;\n }\n }\n\n // Disabled state sets text to gray and nukes hover/tab effects\n &.disabled > a {\n color: @nav-disabled-link-color;\n\n &:hover,\n &:focus {\n color: @nav-disabled-link-hover-color;\n text-decoration: none;\n background-color: transparent;\n cursor: @cursor-disabled;\n }\n }\n }\n\n // Open dropdowns\n .open > a {\n &,\n &:hover,\n &:focus {\n background-color: @nav-link-hover-bg;\n border-color: @link-color;\n }\n }\n\n // Nav dividers (deprecated with v3.0.1)\n //\n // This should have been removed in v3 with the dropping of `.nav-list`, but\n // we missed it. We don't currently support this anywhere, but in the interest\n // of maintaining backward compatibility in case you use it, it's deprecated.\n .nav-divider {\n .nav-divider();\n }\n\n // Prevent IE8 from misplacing imgs\n //\n // See https://github.com/h5bp/html5-boilerplate/issues/984#issuecomment-3985989\n > li > a > img {\n max-width: none;\n }\n}\n\n\n// Tabs\n// -------------------------\n\n// Give the tabs something to sit on\n.nav-tabs {\n border-bottom: 1px solid @nav-tabs-border-color;\n > li {\n float: left;\n // Make the list-items overlay the bottom border\n margin-bottom: -1px;\n\n // Actual tabs (as links)\n > a {\n margin-right: 2px;\n line-height: @line-height-base;\n border: 1px solid transparent;\n border-radius: @border-radius-base @border-radius-base 0 0;\n &:hover {\n border-color: @nav-tabs-link-hover-border-color @nav-tabs-link-hover-border-color @nav-tabs-border-color;\n }\n }\n\n // Active state, and its :hover to override normal :hover\n &.active > a {\n &,\n &:hover,\n &:focus {\n color: @nav-tabs-active-link-hover-color;\n background-color: @nav-tabs-active-link-hover-bg;\n border: 1px solid @nav-tabs-active-link-hover-border-color;\n border-bottom-color: transparent;\n cursor: default;\n }\n }\n }\n // pulling this in mainly for less shorthand\n &.nav-justified {\n .nav-justified();\n .nav-tabs-justified();\n }\n}\n\n\n// Pills\n// -------------------------\n.nav-pills {\n > li {\n float: left;\n\n // Links rendered as pills\n > a {\n border-radius: @nav-pills-border-radius;\n }\n + li {\n margin-left: 2px;\n }\n\n // Active state\n &.active > a {\n &,\n &:hover,\n &:focus {\n color: @nav-pills-active-link-hover-color;\n background-color: @nav-pills-active-link-hover-bg;\n }\n }\n }\n}\n\n\n// Stacked pills\n.nav-stacked {\n > li {\n float: none;\n + li {\n margin-top: 2px;\n margin-left: 0; // no need for this gap between nav items\n }\n }\n}\n\n\n// Nav variations\n// --------------------------------------------------\n\n// Justified nav links\n// -------------------------\n\n.nav-justified {\n width: 100%;\n\n > li {\n float: none;\n > a {\n text-align: center;\n margin-bottom: 5px;\n }\n }\n\n > .dropdown .dropdown-menu {\n top: auto;\n left: auto;\n }\n\n @media (min-width: @screen-sm-min) {\n > li {\n display: table-cell;\n width: 1%;\n > a {\n margin-bottom: 0;\n }\n }\n }\n}\n\n// Move borders to anchors instead of bottom of list\n//\n// Mixin for adding on top the shared `.nav-justified` styles for our tabs\n.nav-tabs-justified {\n border-bottom: 0;\n\n > li > a {\n // Override margin from .nav-tabs\n margin-right: 0;\n border-radius: @border-radius-base;\n }\n\n > .active > a,\n > .active > a:hover,\n > .active > a:focus {\n border: 1px solid @nav-tabs-justified-link-border-color;\n }\n\n @media (min-width: @screen-sm-min) {\n > li > a {\n border-bottom: 1px solid @nav-tabs-justified-link-border-color;\n border-radius: @border-radius-base @border-radius-base 0 0;\n }\n > .active > a,\n > .active > a:hover,\n > .active > a:focus {\n border-bottom-color: @nav-tabs-justified-active-link-border-color;\n }\n }\n}\n\n\n// Tabbable tabs\n// -------------------------\n\n// Hide tabbable panes to start, show them when `.active`\n.tab-content {\n > .tab-pane {\n display: none;\n }\n > .active {\n display: block;\n }\n}\n\n\n// Dropdowns\n// -------------------------\n\n// Specific dropdowns\n.nav-tabs .dropdown-menu {\n // make dropdown border overlap tab border\n margin-top: -1px;\n // Remove the top rounded corners here since there is a hard edge above the menu\n .border-top-radius(0);\n}\n","//\n// Navbars\n// --------------------------------------------------\n\n\n// Wrapper and base class\n//\n// Provide a static navbar from which we expand to create full-width, fixed, and\n// other navbar variations.\n\n.navbar {\n position: relative;\n min-height: @navbar-height; // Ensure a navbar always shows (e.g., without a .navbar-brand in collapsed mode)\n margin-bottom: @navbar-margin-bottom;\n border: 1px solid transparent;\n\n // Prevent floats from breaking the navbar\n &:extend(.clearfix all);\n\n @media (min-width: @grid-float-breakpoint) {\n border-radius: @navbar-border-radius;\n }\n}\n\n\n// Navbar heading\n//\n// Groups `.navbar-brand` and `.navbar-toggle` into a single component for easy\n// styling of responsive aspects.\n\n.navbar-header {\n &:extend(.clearfix all);\n\n @media (min-width: @grid-float-breakpoint) {\n float: left;\n }\n}\n\n\n// Navbar collapse (body)\n//\n// Group your navbar content into this for easy collapsing and expanding across\n// various device sizes. By default, this content is collapsed when <768px, but\n// will expand past that for a horizontal display.\n//\n// To start (on mobile devices) the navbar links, forms, and buttons are stacked\n// vertically and include a `max-height` to overflow in case you have too much\n// content for the user's viewport.\n\n.navbar-collapse {\n overflow-x: visible;\n padding-right: @navbar-padding-horizontal;\n padding-left: @navbar-padding-horizontal;\n border-top: 1px solid transparent;\n box-shadow: inset 0 1px 0 rgba(255,255,255,.1);\n &:extend(.clearfix all);\n -webkit-overflow-scrolling: touch;\n\n &.in {\n overflow-y: auto;\n }\n\n @media (min-width: @grid-float-breakpoint) {\n width: auto;\n border-top: 0;\n box-shadow: none;\n\n &.collapse {\n display: block !important;\n height: auto !important;\n padding-bottom: 0; // Override default setting\n overflow: visible !important;\n }\n\n &.in {\n overflow-y: visible;\n }\n\n // Undo the collapse side padding for navbars with containers to ensure\n // alignment of right-aligned contents.\n .navbar-fixed-top &,\n .navbar-static-top &,\n .navbar-fixed-bottom & {\n padding-left: 0;\n padding-right: 0;\n }\n }\n}\n\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n .navbar-collapse {\n max-height: @navbar-collapse-max-height;\n\n @media (max-device-width: @screen-xs-min) and (orientation: landscape) {\n max-height: 200px;\n }\n }\n}\n\n\n// Both navbar header and collapse\n//\n// When a container is present, change the behavior of the header and collapse.\n\n.container,\n.container-fluid {\n > .navbar-header,\n > .navbar-collapse {\n margin-right: -@navbar-padding-horizontal;\n margin-left: -@navbar-padding-horizontal;\n\n @media (min-width: @grid-float-breakpoint) {\n margin-right: 0;\n margin-left: 0;\n }\n }\n}\n\n\n//\n// Navbar alignment options\n//\n// Display the navbar across the entirety of the page or fixed it to the top or\n// bottom of the page.\n\n// Static top (unfixed, but 100% wide) navbar\n.navbar-static-top {\n z-index: @zindex-navbar;\n border-width: 0 0 1px;\n\n @media (min-width: @grid-float-breakpoint) {\n border-radius: 0;\n }\n}\n\n// Fix the top/bottom navbars when screen real estate supports it\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n position: fixed;\n right: 0;\n left: 0;\n z-index: @zindex-navbar-fixed;\n\n // Undo the rounded corners\n @media (min-width: @grid-float-breakpoint) {\n border-radius: 0;\n }\n}\n.navbar-fixed-top {\n top: 0;\n border-width: 0 0 1px;\n}\n.navbar-fixed-bottom {\n bottom: 0;\n margin-bottom: 0; // override .navbar defaults\n border-width: 1px 0 0;\n}\n\n\n// Brand/project name\n\n.navbar-brand {\n float: left;\n padding: @navbar-padding-vertical @navbar-padding-horizontal;\n font-size: @font-size-large;\n line-height: @line-height-computed;\n height: @navbar-height;\n\n &:hover,\n &:focus {\n text-decoration: none;\n }\n\n > img {\n display: block;\n }\n\n @media (min-width: @grid-float-breakpoint) {\n .navbar > .container &,\n .navbar > .container-fluid & {\n margin-left: -@navbar-padding-horizontal;\n }\n }\n}\n\n\n// Navbar toggle\n//\n// Custom button for toggling the `.navbar-collapse`, powered by the collapse\n// JavaScript plugin.\n\n.navbar-toggle {\n position: relative;\n float: right;\n margin-right: @navbar-padding-horizontal;\n padding: 9px 10px;\n .navbar-vertical-align(34px);\n background-color: transparent;\n background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n border: 1px solid transparent;\n border-radius: @border-radius-base;\n\n // We remove the `outline` here, but later compensate by attaching `:hover`\n // styles to `:focus`.\n &:focus {\n outline: 0;\n }\n\n // Bars\n .icon-bar {\n display: block;\n width: 22px;\n height: 2px;\n border-radius: 1px;\n }\n .icon-bar + .icon-bar {\n margin-top: 4px;\n }\n\n @media (min-width: @grid-float-breakpoint) {\n display: none;\n }\n}\n\n\n// Navbar nav links\n//\n// Builds on top of the `.nav` components with its own modifier class to make\n// the nav the full height of the horizontal nav (above 768px).\n\n.navbar-nav {\n margin: (@navbar-padding-vertical / 2) -@navbar-padding-horizontal;\n\n > li > a {\n padding-top: 10px;\n padding-bottom: 10px;\n line-height: @line-height-computed;\n }\n\n @media (max-width: @grid-float-breakpoint-max) {\n // Dropdowns get custom display when collapsed\n .open .dropdown-menu {\n position: static;\n float: none;\n width: auto;\n margin-top: 0;\n background-color: transparent;\n border: 0;\n box-shadow: none;\n > li > a,\n .dropdown-header {\n padding: 5px 15px 5px 25px;\n }\n > li > a {\n line-height: @line-height-computed;\n &:hover,\n &:focus {\n background-image: none;\n }\n }\n }\n }\n\n // Uncollapse the nav\n @media (min-width: @grid-float-breakpoint) {\n float: left;\n margin: 0;\n\n > li {\n float: left;\n > a {\n padding-top: @navbar-padding-vertical;\n padding-bottom: @navbar-padding-vertical;\n }\n }\n }\n}\n\n\n// Navbar form\n//\n// Extension of the `.form-inline` with some extra flavor for optimum display in\n// our navbars.\n\n.navbar-form {\n margin-left: -@navbar-padding-horizontal;\n margin-right: -@navbar-padding-horizontal;\n padding: 10px @navbar-padding-horizontal;\n border-top: 1px solid transparent;\n border-bottom: 1px solid transparent;\n @shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1);\n .box-shadow(@shadow);\n\n // Mixin behavior for optimum display\n .form-inline();\n\n .form-group {\n @media (max-width: @grid-float-breakpoint-max) {\n margin-bottom: 5px;\n\n &:last-child {\n margin-bottom: 0;\n }\n }\n }\n\n // Vertically center in expanded, horizontal navbar\n .navbar-vertical-align(@input-height-base);\n\n // Undo 100% width for pull classes\n @media (min-width: @grid-float-breakpoint) {\n width: auto;\n border: 0;\n margin-left: 0;\n margin-right: 0;\n padding-top: 0;\n padding-bottom: 0;\n .box-shadow(none);\n }\n}\n\n\n// Dropdown menus\n\n// Menu position and menu carets\n.navbar-nav > li > .dropdown-menu {\n margin-top: 0;\n .border-top-radius(0);\n}\n// Menu position and menu caret support for dropups via extra dropup class\n.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {\n margin-bottom: 0;\n .border-top-radius(@navbar-border-radius);\n .border-bottom-radius(0);\n}\n\n\n// Buttons in navbars\n//\n// Vertically center a button within a navbar (when *not* in a form).\n\n.navbar-btn {\n .navbar-vertical-align(@input-height-base);\n\n &.btn-sm {\n .navbar-vertical-align(@input-height-small);\n }\n &.btn-xs {\n .navbar-vertical-align(22);\n }\n}\n\n\n// Text in navbars\n//\n// Add a class to make any element properly align itself vertically within the navbars.\n\n.navbar-text {\n .navbar-vertical-align(@line-height-computed);\n\n @media (min-width: @grid-float-breakpoint) {\n float: left;\n margin-left: @navbar-padding-horizontal;\n margin-right: @navbar-padding-horizontal;\n }\n}\n\n\n// Component alignment\n//\n// Repurpose the pull utilities as their own navbar utilities to avoid specificity\n// issues with parents and chaining. Only do this when the navbar is uncollapsed\n// though so that navbar contents properly stack and align in mobile.\n//\n// Declared after the navbar components to ensure more specificity on the margins.\n\n@media (min-width: @grid-float-breakpoint) {\n .navbar-left { .pull-left(); }\n .navbar-right {\n .pull-right();\n margin-right: -@navbar-padding-horizontal;\n\n ~ .navbar-right {\n margin-right: 0;\n }\n }\n}\n\n\n// Alternate navbars\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n background-color: @navbar-default-bg;\n border-color: @navbar-default-border;\n\n .navbar-brand {\n color: @navbar-default-brand-color;\n &:hover,\n &:focus {\n color: @navbar-default-brand-hover-color;\n background-color: @navbar-default-brand-hover-bg;\n }\n }\n\n .navbar-text {\n color: @navbar-default-color;\n }\n\n .navbar-nav {\n > li > a {\n color: @navbar-default-link-color;\n\n &:hover,\n &:focus {\n color: @navbar-default-link-hover-color;\n background-color: @navbar-default-link-hover-bg;\n }\n }\n > .active > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-default-link-active-color;\n background-color: @navbar-default-link-active-bg;\n }\n }\n > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-default-link-disabled-color;\n background-color: @navbar-default-link-disabled-bg;\n }\n }\n }\n\n .navbar-toggle {\n border-color: @navbar-default-toggle-border-color;\n &:hover,\n &:focus {\n background-color: @navbar-default-toggle-hover-bg;\n }\n .icon-bar {\n background-color: @navbar-default-toggle-icon-bar-bg;\n }\n }\n\n .navbar-collapse,\n .navbar-form {\n border-color: @navbar-default-border;\n }\n\n // Dropdown menu items\n .navbar-nav {\n // Remove background color from open dropdown\n > .open > a {\n &,\n &:hover,\n &:focus {\n background-color: @navbar-default-link-active-bg;\n color: @navbar-default-link-active-color;\n }\n }\n\n @media (max-width: @grid-float-breakpoint-max) {\n // Dropdowns get custom display when collapsed\n .open .dropdown-menu {\n > li > a {\n color: @navbar-default-link-color;\n &:hover,\n &:focus {\n color: @navbar-default-link-hover-color;\n background-color: @navbar-default-link-hover-bg;\n }\n }\n > .active > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-default-link-active-color;\n background-color: @navbar-default-link-active-bg;\n }\n }\n > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-default-link-disabled-color;\n background-color: @navbar-default-link-disabled-bg;\n }\n }\n }\n }\n }\n\n\n // Links in navbars\n //\n // Add a class to ensure links outside the navbar nav are colored correctly.\n\n .navbar-link {\n color: @navbar-default-link-color;\n &:hover {\n color: @navbar-default-link-hover-color;\n }\n }\n\n .btn-link {\n color: @navbar-default-link-color;\n &:hover,\n &:focus {\n color: @navbar-default-link-hover-color;\n }\n &[disabled],\n fieldset[disabled] & {\n &:hover,\n &:focus {\n color: @navbar-default-link-disabled-color;\n }\n }\n }\n}\n\n// Inverse navbar\n\n.navbar-inverse {\n background-color: @navbar-inverse-bg;\n border-color: @navbar-inverse-border;\n\n .navbar-brand {\n color: @navbar-inverse-brand-color;\n &:hover,\n &:focus {\n color: @navbar-inverse-brand-hover-color;\n background-color: @navbar-inverse-brand-hover-bg;\n }\n }\n\n .navbar-text {\n color: @navbar-inverse-color;\n }\n\n .navbar-nav {\n > li > a {\n color: @navbar-inverse-link-color;\n\n &:hover,\n &:focus {\n color: @navbar-inverse-link-hover-color;\n background-color: @navbar-inverse-link-hover-bg;\n }\n }\n > .active > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-inverse-link-active-color;\n background-color: @navbar-inverse-link-active-bg;\n }\n }\n > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-inverse-link-disabled-color;\n background-color: @navbar-inverse-link-disabled-bg;\n }\n }\n }\n\n // Darken the responsive nav toggle\n .navbar-toggle {\n border-color: @navbar-inverse-toggle-border-color;\n &:hover,\n &:focus {\n background-color: @navbar-inverse-toggle-hover-bg;\n }\n .icon-bar {\n background-color: @navbar-inverse-toggle-icon-bar-bg;\n }\n }\n\n .navbar-collapse,\n .navbar-form {\n border-color: darken(@navbar-inverse-bg, 7%);\n }\n\n // Dropdowns\n .navbar-nav {\n > .open > a {\n &,\n &:hover,\n &:focus {\n background-color: @navbar-inverse-link-active-bg;\n color: @navbar-inverse-link-active-color;\n }\n }\n\n @media (max-width: @grid-float-breakpoint-max) {\n // Dropdowns get custom display\n .open .dropdown-menu {\n > .dropdown-header {\n border-color: @navbar-inverse-border;\n }\n .divider {\n background-color: @navbar-inverse-border;\n }\n > li > a {\n color: @navbar-inverse-link-color;\n &:hover,\n &:focus {\n color: @navbar-inverse-link-hover-color;\n background-color: @navbar-inverse-link-hover-bg;\n }\n }\n > .active > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-inverse-link-active-color;\n background-color: @navbar-inverse-link-active-bg;\n }\n }\n > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-inverse-link-disabled-color;\n background-color: @navbar-inverse-link-disabled-bg;\n }\n }\n }\n }\n }\n\n .navbar-link {\n color: @navbar-inverse-link-color;\n &:hover {\n color: @navbar-inverse-link-hover-color;\n }\n }\n\n .btn-link {\n color: @navbar-inverse-link-color;\n &:hover,\n &:focus {\n color: @navbar-inverse-link-hover-color;\n }\n &[disabled],\n fieldset[disabled] & {\n &:hover,\n &:focus {\n color: @navbar-inverse-link-disabled-color;\n }\n }\n }\n}\n","// Navbar vertical align\n//\n// Vertically center elements in the navbar.\n// Example: an element has a height of 30px, so write out `.navbar-vertical-align(30px);` to calculate the appropriate top margin.\n\n.navbar-vertical-align(@element-height) {\n margin-top: ((@navbar-height - @element-height) / 2);\n margin-bottom: ((@navbar-height - @element-height) / 2);\n}\n","//\n// Utility classes\n// --------------------------------------------------\n\n\n// Floats\n// -------------------------\n\n.clearfix {\n .clearfix();\n}\n.center-block {\n .center-block();\n}\n.pull-right {\n float: right !important;\n}\n.pull-left {\n float: left !important;\n}\n\n\n// Toggling content\n// -------------------------\n\n// Note: Deprecated .hide in favor of .hidden or .sr-only (as appropriate) in v3.0.1\n.hide {\n display: none !important;\n}\n.show {\n display: block !important;\n}\n.invisible {\n visibility: hidden;\n}\n.text-hide {\n .text-hide();\n}\n\n\n// Hide from screenreaders and browsers\n//\n// Credit: HTML5 Boilerplate\n\n.hidden {\n display: none !important;\n}\n\n\n// For Affix plugin\n// -------------------------\n\n.affix {\n position: fixed;\n}\n","//\n// Breadcrumbs\n// --------------------------------------------------\n\n\n.breadcrumb {\n padding: @breadcrumb-padding-vertical @breadcrumb-padding-horizontal;\n margin-bottom: @line-height-computed;\n list-style: none;\n background-color: @breadcrumb-bg;\n border-radius: @border-radius-base;\n\n > li {\n display: inline-block;\n\n + li:before {\n content: \"@{breadcrumb-separator}\\00a0\"; // Unicode space added since inline-block means non-collapsing white-space\n padding: 0 5px;\n color: @breadcrumb-color;\n }\n }\n\n > .active {\n color: @breadcrumb-active-color;\n }\n}\n","//\n// Pagination (multiple pages)\n// --------------------------------------------------\n.pagination {\n display: inline-block;\n padding-left: 0;\n margin: @line-height-computed 0;\n border-radius: @border-radius-base;\n\n > li {\n display: inline; // Remove list-style and block-level defaults\n > a,\n > span {\n position: relative;\n float: left; // Collapse white-space\n padding: @padding-base-vertical @padding-base-horizontal;\n line-height: @line-height-base;\n text-decoration: none;\n color: @pagination-color;\n background-color: @pagination-bg;\n border: 1px solid @pagination-border;\n margin-left: -1px;\n }\n &:first-child {\n > a,\n > span {\n margin-left: 0;\n .border-left-radius(@border-radius-base);\n }\n }\n &:last-child {\n > a,\n > span {\n .border-right-radius(@border-radius-base);\n }\n }\n }\n\n > li > a,\n > li > span {\n &:hover,\n &:focus {\n z-index: 2;\n color: @pagination-hover-color;\n background-color: @pagination-hover-bg;\n border-color: @pagination-hover-border;\n }\n }\n\n > .active > a,\n > .active > span {\n &,\n &:hover,\n &:focus {\n z-index: 3;\n color: @pagination-active-color;\n background-color: @pagination-active-bg;\n border-color: @pagination-active-border;\n cursor: default;\n }\n }\n\n > .disabled {\n > span,\n > span:hover,\n > span:focus,\n > a,\n > a:hover,\n > a:focus {\n color: @pagination-disabled-color;\n background-color: @pagination-disabled-bg;\n border-color: @pagination-disabled-border;\n cursor: @cursor-disabled;\n }\n }\n}\n\n// Sizing\n// --------------------------------------------------\n\n// Large\n.pagination-lg {\n .pagination-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @border-radius-large);\n}\n\n// Small\n.pagination-sm {\n .pagination-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @border-radius-small);\n}\n","// Pagination\n\n.pagination-size(@padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {\n > li {\n > a,\n > span {\n padding: @padding-vertical @padding-horizontal;\n font-size: @font-size;\n line-height: @line-height;\n }\n &:first-child {\n > a,\n > span {\n .border-left-radius(@border-radius);\n }\n }\n &:last-child {\n > a,\n > span {\n .border-right-radius(@border-radius);\n }\n }\n }\n}\n","//\n// Pager pagination\n// --------------------------------------------------\n\n\n.pager {\n padding-left: 0;\n margin: @line-height-computed 0;\n list-style: none;\n text-align: center;\n &:extend(.clearfix all);\n li {\n display: inline;\n > a,\n > span {\n display: inline-block;\n padding: 5px 14px;\n background-color: @pager-bg;\n border: 1px solid @pager-border;\n border-radius: @pager-border-radius;\n }\n\n > a:hover,\n > a:focus {\n text-decoration: none;\n background-color: @pager-hover-bg;\n }\n }\n\n .next {\n > a,\n > span {\n float: right;\n }\n }\n\n .previous {\n > a,\n > span {\n float: left;\n }\n }\n\n .disabled {\n > a,\n > a:hover,\n > a:focus,\n > span {\n color: @pager-disabled-color;\n background-color: @pager-bg;\n cursor: @cursor-disabled;\n }\n }\n}\n","//\n// Labels\n// --------------------------------------------------\n\n.label {\n display: inline;\n padding: .2em .6em .3em;\n font-size: 75%;\n font-weight: bold;\n line-height: 1;\n color: @label-color;\n text-align: center;\n white-space: nowrap;\n vertical-align: baseline;\n border-radius: .25em;\n\n // Add hover effects, but only for links\n a& {\n &:hover,\n &:focus {\n color: @label-link-hover-color;\n text-decoration: none;\n cursor: pointer;\n }\n }\n\n // Empty labels collapse automatically (not available in IE8)\n &:empty {\n display: none;\n }\n\n // Quick fix for labels in buttons\n .btn & {\n position: relative;\n top: -1px;\n }\n}\n\n// Colors\n// Contextual variations (linked labels get darker on :hover)\n\n.label-default {\n .label-variant(@label-default-bg);\n}\n\n.label-primary {\n .label-variant(@label-primary-bg);\n}\n\n.label-success {\n .label-variant(@label-success-bg);\n}\n\n.label-info {\n .label-variant(@label-info-bg);\n}\n\n.label-warning {\n .label-variant(@label-warning-bg);\n}\n\n.label-danger {\n .label-variant(@label-danger-bg);\n}\n","// Labels\n\n.label-variant(@color) {\n background-color: @color;\n\n &[href] {\n &:hover,\n &:focus {\n background-color: darken(@color, 10%);\n }\n }\n}\n","//\n// Badges\n// --------------------------------------------------\n\n\n// Base class\n.badge {\n display: inline-block;\n min-width: 10px;\n padding: 3px 7px;\n font-size: @font-size-small;\n font-weight: @badge-font-weight;\n color: @badge-color;\n line-height: @badge-line-height;\n vertical-align: middle;\n white-space: nowrap;\n text-align: center;\n background-color: @badge-bg;\n border-radius: @badge-border-radius;\n\n // Empty badges collapse automatically (not available in IE8)\n &:empty {\n display: none;\n }\n\n // Quick fix for badges in buttons\n .btn & {\n position: relative;\n top: -1px;\n }\n\n .btn-xs &,\n .btn-group-xs > .btn & {\n top: 0;\n padding: 1px 5px;\n }\n\n // Hover state, but only for links\n a& {\n &:hover,\n &:focus {\n color: @badge-link-hover-color;\n text-decoration: none;\n cursor: pointer;\n }\n }\n\n // Account for badges in navs\n .list-group-item.active > &,\n .nav-pills > .active > a > & {\n color: @badge-active-color;\n background-color: @badge-active-bg;\n }\n\n .list-group-item > & {\n float: right;\n }\n\n .list-group-item > & + & {\n margin-right: 5px;\n }\n\n .nav-pills > li > a > & {\n margin-left: 3px;\n }\n}\n","//\n// Jumbotron\n// --------------------------------------------------\n\n\n.jumbotron {\n padding-top: @jumbotron-padding;\n padding-bottom: @jumbotron-padding;\n margin-bottom: @jumbotron-padding;\n color: @jumbotron-color;\n background-color: @jumbotron-bg;\n\n h1,\n .h1 {\n color: @jumbotron-heading-color;\n }\n\n p {\n margin-bottom: (@jumbotron-padding / 2);\n font-size: @jumbotron-font-size;\n font-weight: 200;\n }\n\n > hr {\n border-top-color: darken(@jumbotron-bg, 10%);\n }\n\n .container &,\n .container-fluid & {\n border-radius: @border-radius-large; // Only round corners at higher resolutions if contained in a container\n padding-left: (@grid-gutter-width / 2);\n padding-right: (@grid-gutter-width / 2);\n }\n\n .container {\n max-width: 100%;\n }\n\n @media screen and (min-width: @screen-sm-min) {\n padding-top: (@jumbotron-padding * 1.6);\n padding-bottom: (@jumbotron-padding * 1.6);\n\n .container &,\n .container-fluid & {\n padding-left: (@jumbotron-padding * 2);\n padding-right: (@jumbotron-padding * 2);\n }\n\n h1,\n .h1 {\n font-size: @jumbotron-heading-font-size;\n }\n }\n}\n","//\n// Thumbnails\n// --------------------------------------------------\n\n\n// Mixin and adjust the regular image class\n.thumbnail {\n display: block;\n padding: @thumbnail-padding;\n margin-bottom: @line-height-computed;\n line-height: @line-height-base;\n background-color: @thumbnail-bg;\n border: 1px solid @thumbnail-border;\n border-radius: @thumbnail-border-radius;\n .transition(border .2s ease-in-out);\n\n > img,\n a > img {\n &:extend(.img-responsive);\n margin-left: auto;\n margin-right: auto;\n }\n\n // Add a hover state for linked versions only\n a&:hover,\n a&:focus,\n a&.active {\n border-color: @link-color;\n }\n\n // Image captions\n .caption {\n padding: @thumbnail-caption-padding;\n color: @thumbnail-caption-color;\n }\n}\n","//\n// Alerts\n// --------------------------------------------------\n\n\n// Base styles\n// -------------------------\n\n.alert {\n padding: @alert-padding;\n margin-bottom: @line-height-computed;\n border: 1px solid transparent;\n border-radius: @alert-border-radius;\n\n // Headings for larger alerts\n h4 {\n margin-top: 0;\n // Specified for the h4 to prevent conflicts of changing @headings-color\n color: inherit;\n }\n\n // Provide class for links that match alerts\n .alert-link {\n font-weight: @alert-link-font-weight;\n }\n\n // Improve alignment and spacing of inner content\n > p,\n > ul {\n margin-bottom: 0;\n }\n\n > p + p {\n margin-top: 5px;\n }\n}\n\n// Dismissible alerts\n//\n// Expand the right padding and account for the close button's positioning.\n\n.alert-dismissable, // The misspelled .alert-dismissable was deprecated in 3.2.0.\n.alert-dismissible {\n padding-right: (@alert-padding + 20);\n\n // Adjust close link position\n .close {\n position: relative;\n top: -2px;\n right: -21px;\n color: inherit;\n }\n}\n\n// Alternate styles\n//\n// Generate contextual modifier classes for colorizing the alert.\n\n.alert-success {\n .alert-variant(@alert-success-bg; @alert-success-border; @alert-success-text);\n}\n\n.alert-info {\n .alert-variant(@alert-info-bg; @alert-info-border; @alert-info-text);\n}\n\n.alert-warning {\n .alert-variant(@alert-warning-bg; @alert-warning-border; @alert-warning-text);\n}\n\n.alert-danger {\n .alert-variant(@alert-danger-bg; @alert-danger-border; @alert-danger-text);\n}\n","// Alerts\n\n.alert-variant(@background; @border; @text-color) {\n background-color: @background;\n border-color: @border;\n color: @text-color;\n\n hr {\n border-top-color: darken(@border, 5%);\n }\n .alert-link {\n color: darken(@text-color, 10%);\n }\n}\n","//\n// Progress bars\n// --------------------------------------------------\n\n\n// Bar animations\n// -------------------------\n\n// WebKit\n@-webkit-keyframes progress-bar-stripes {\n from { background-position: 40px 0; }\n to { background-position: 0 0; }\n}\n\n// Spec and IE10+\n@keyframes progress-bar-stripes {\n from { background-position: 40px 0; }\n to { background-position: 0 0; }\n}\n\n\n// Bar itself\n// -------------------------\n\n// Outer container\n.progress {\n overflow: hidden;\n height: @line-height-computed;\n margin-bottom: @line-height-computed;\n background-color: @progress-bg;\n border-radius: @progress-border-radius;\n .box-shadow(inset 0 1px 2px rgba(0,0,0,.1));\n}\n\n// Bar of progress\n.progress-bar {\n float: left;\n width: 0%;\n height: 100%;\n font-size: @font-size-small;\n line-height: @line-height-computed;\n color: @progress-bar-color;\n text-align: center;\n background-color: @progress-bar-bg;\n .box-shadow(inset 0 -1px 0 rgba(0,0,0,.15));\n .transition(width .6s ease);\n}\n\n// Striped bars\n//\n// `.progress-striped .progress-bar` is deprecated as of v3.2.0 in favor of the\n// `.progress-bar-striped` class, which you just add to an existing\n// `.progress-bar`.\n.progress-striped .progress-bar,\n.progress-bar-striped {\n #gradient > .striped();\n background-size: 40px 40px;\n}\n\n// Call animation for the active one\n//\n// `.progress.active .progress-bar` is deprecated as of v3.2.0 in favor of the\n// `.progress-bar.active` approach.\n.progress.active .progress-bar,\n.progress-bar.active {\n .animation(progress-bar-stripes 2s linear infinite);\n}\n\n\n// Variations\n// -------------------------\n\n.progress-bar-success {\n .progress-bar-variant(@progress-bar-success-bg);\n}\n\n.progress-bar-info {\n .progress-bar-variant(@progress-bar-info-bg);\n}\n\n.progress-bar-warning {\n .progress-bar-variant(@progress-bar-warning-bg);\n}\n\n.progress-bar-danger {\n .progress-bar-variant(@progress-bar-danger-bg);\n}\n","// Gradients\n\n#gradient {\n\n // Horizontal gradient, from left to right\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n // Vertical gradient, from top to bottom\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n background-repeat: repeat-x;\n background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n }\n .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .radial(@inner-color: #555; @outer-color: #333) {\n background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n background-image: radial-gradient(circle, @inner-color, @outer-color);\n background-repeat: no-repeat;\n }\n .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n }\n}\n","// Progress bars\n\n.progress-bar-variant(@color) {\n background-color: @color;\n\n // Deprecated parent class requirement as of v3.2.0\n .progress-striped & {\n #gradient > .striped();\n }\n}\n",".media {\n // Proper spacing between instances of .media\n margin-top: 15px;\n\n &:first-child {\n margin-top: 0;\n }\n}\n\n.media,\n.media-body {\n zoom: 1;\n overflow: hidden;\n}\n\n.media-body {\n width: 10000px;\n}\n\n.media-object {\n display: block;\n\n // Fix collapse in webkit from max-width: 100% and display: table-cell.\n &.img-thumbnail {\n max-width: none;\n }\n}\n\n.media-right,\n.media > .pull-right {\n padding-left: 10px;\n}\n\n.media-left,\n.media > .pull-left {\n padding-right: 10px;\n}\n\n.media-left,\n.media-right,\n.media-body {\n display: table-cell;\n vertical-align: top;\n}\n\n.media-middle {\n vertical-align: middle;\n}\n\n.media-bottom {\n vertical-align: bottom;\n}\n\n// Reset margins on headings for tighter default spacing\n.media-heading {\n margin-top: 0;\n margin-bottom: 5px;\n}\n\n// Media list variation\n//\n// Undo default ul/ol styles\n.media-list {\n padding-left: 0;\n list-style: none;\n}\n","//\n// List groups\n// --------------------------------------------------\n\n\n// Base class\n//\n// Easily usable on
+ diff --git a/templates/munstrap/templates/munin-problemview.tmpl b/templates/munstrap/templates/munin-problemview.tmpl index 60ddc972..0adea867 100644 --- a/templates/munstrap/templates/munin-problemview.tmpl +++ b/templates/munstrap/templates/munin-problemview.tmpl @@ -92,4 +92,5 @@ - + + \ No newline at end of file diff --git a/templates/munstrap/templates/munin-serviceview.tmpl b/templates/munstrap/templates/munin-serviceview.tmpl index 3998e461..75231167 100644 --- a/templates/munstrap/templates/munin-serviceview.tmpl +++ b/templates/munstrap/templates/munin-serviceview.tmpl @@ -195,10 +195,12 @@ - - - - + + + + + + - - + + \ No newline at end of file diff --git a/templates/munstrap/templates/partial/footer.tmpl b/templates/munstrap/templates/partial/footer.tmpl index 62263e48..1b0d120c 100644 --- a/templates/munstrap/templates/partial/footer.tmpl +++ b/templates/munstrap/templates/partial/footer.tmpl @@ -1,28 +1,3 @@ - -
-
- This page was generated by Munin version at with MunStrap template. -
-
- - - - - + \ No newline at end of file diff --git a/templates/munstrap/templates/partial/footer_pre.tmpl b/templates/munstrap/templates/partial/footer_pre.tmpl new file mode 100644 index 00000000..406d5aa5 --- /dev/null +++ b/templates/munstrap/templates/partial/footer_pre.tmpl @@ -0,0 +1,16 @@ + + +
+
+ + This page was generated by Munin + version at + with MunStrap template. + +
+
+ + + + + diff --git a/templates/munstrap/templates/partial/generated_by.tmpl b/templates/munstrap/templates/partial/generated_by.tmpl index cef2100b..2755029a 100644 --- a/templates/munstrap/templates/partial/generated_by.tmpl +++ b/templates/munstrap/templates/partial/generated_by.tmpl @@ -1,5 +1,5 @@ - +
-
+ \ No newline at end of file diff --git a/templates/munstrap/templates/partial/head.tmpl b/templates/munstrap/templates/partial/head.tmpl index 9250da4f..0cefcf46 100644 --- a/templates/munstrap/templates/partial/head.tmpl +++ b/templates/munstrap/templates/partial/head.tmpl @@ -2,7 +2,7 @@ Munin <TMPL_LOOP NAME="PATH"><TMPL_IF NAME="pathname"> :: <TMPL_VAR ESCAPE="HTML" NAME="pathname"><TMPL_ELSE>Munin</TMPL_IF></TMPL_LOOP> - + @@ -12,10 +12,9 @@ /static/img/favicon.ico" /> /static/img/favicon.png" /> /static/css/bootstrap.min.css" rel="stylesheet" /> - /static/css/style-munstrap.css" rel="stylesheet" /> - + /static/css/style-munstrap.min.css" rel="stylesheet" /> - + - -
- + +
- diff --git a/templates/munstrap/templates/partial/path.tmpl b/templates/munstrap/templates/partial/path.tmpl index fb27324c..49fa8630 100644 --- a/templates/munstrap/templates/partial/path.tmpl +++ b/templates/munstrap/templates/partial/path.tmpl @@ -1 +1,19 @@ - :: ">">Overview + + :: + + "> + + + + + + + + "> + + Overview + + + + + diff --git a/templates/official/munin-nodeview.tmpl b/templates/official/munin-nodeview.tmpl index e9b852df..a1b5f5ba 100644 --- a/templates/official/munin-nodeview.tmpl +++ b/templates/official/munin-nodeview.tmpl @@ -17,14 +17,28 @@ width="" height=""/> - "> + "> warncrit src="" alt="" width="" height=""/> - + "> + warncrit + src="" + alt="" + width="" + height=""/> + + "> + warncrit + src="" + alt="" + width="" + height=""/> + + diff --git a/templates/official/munin-serviceview.tmpl b/templates/official/munin-serviceview.tmpl index a89d1375..755931ed 100644 --- a/templates/official/munin-serviceview.tmpl +++ b/templates/official/munin-serviceview.tmpl @@ -57,7 +57,7 @@

Graph Information

- +

Note: diff --git a/templates/official/partial/head.tmpl b/templates/official/partial/head.tmpl index 44a8157f..1a7c2342 100644 --- a/templates/official/partial/head.tmpl +++ b/templates/official/partial/head.tmpl @@ -18,4 +18,26 @@ /static/favicon.ico" /> /static/favicon.ico"/> + + diff --git a/tools/munin-node-c/.gitignore b/tools/munin-node-c/.gitignore deleted file mode 100644 index 91f33c2b..00000000 --- a/tools/munin-node-c/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -# output files -/*.o -/munin-node-c -/plugins/* diff --git a/tools/munin-node-from-hell/muninnode-from-hell b/tools/munin-node-from-hell/muninnode-from-hell index 50bda49b..e14e33fd 100755 --- a/tools/munin-node-from-hell/muninnode-from-hell +++ b/tools/munin-node-from-hell/muninnode-from-hell @@ -359,7 +359,7 @@ def main(): % (pluginprofile, tentative_plugin) continue - # support more than one instanciation of the same plugin. + # support more than one instantiation of the same plugin. plugininstancename = tentative_plugin i=2 while (plugins.has_key(plugininstancename)): diff --git a/tools/munin-plugins-busybox/.gitignore b/tools/munin-plugins-busybox/.gitignore deleted file mode 100644 index 5761abcf..00000000 --- a/tools/munin-plugins-busybox/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.o diff --git a/tools/munin2snmp/MUNIN-MIB b/tools/munin2snmp/MUNIN-MIB new file mode 100644 index 00000000..1878802d --- /dev/null +++ b/tools/munin2snmp/MUNIN-MIB @@ -0,0 +1,113 @@ +-- -*- snmpv2 -*- +-- ---------------------------------------------------------------------- +-- MIB file for munin +-- ---------------------------------------------------------------------- +-- +-- Currenly, only statistics are available. + +MUNIN-MIB DEFINITIONS ::= BEGIN + +IMPORTS + OBJECT-TYPE, MODULE-IDENTITY, enterprises, + Counter64 + FROM SNMPv2-SMI + OBJECT-GROUP, MODULE-COMPLIANCE + FROM SNMPv2-CONF + TEXTUAL-CONVENTION + FROM SNMPv2-TC; + +munin MODULE-IDENTITY + LAST-UPDATED "201101060000Z" + ORGANIZATION "BlackMesa" + CONTACT-INFO "GordonFreeman@BlackMesa.mil" + DESCRIPTION + "This MIB module describes information gathered through MUNIN + ioctl for each interface available on the monitored + system. Currently, only statistics are available. Information + may be redundant with what is available in IF-MIB, RMON-MIB, + EtherLike-MIB and some other MIB but they are presented here + without any abstraction." + + REVISION "201101060000Z" + DESCRIPTION "Initial revision." + + ::= { munin2snmp } + +-- We are hosted under The IMS Company OID. +munin2snmp OBJECT IDENTIFIER ::= { enterprises 123456 } + +MuninStatString ::= TEXTUAL-CONVENTION + DISPLAY-HINT "32a" + STATUS current + DESCRIPTION "Name of statistic" + SYNTAX OCTET STRING (SIZE (1..32)) + + +--- +--- muninStatTable +--- + +muninStatTable OBJECT-TYPE + SYNTAX SEQUENCE OF MuninStatEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Statistics from munin" + ::= { munin 100 } + +muninStatEntry OBJECT-TYPE + SYNTAX MuninStatEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Statistic for one interface" + INDEX { IMPLIED muninStatName } + ::= { muninStatTable 1 } + +MuninStatEntry ::= SEQUENCE { + muninStatName MuninStatString, + muninStat Counter64 +} + +muninStatName OBJECT-TYPE + SYNTAX MuninStatString + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Name of the statistic as returned by MUNIN ioctl." + ::= { muninStatEntry 1 } + +muninStat OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Value of the statistic as returned by MUNIN ioctl." + ::= { muninStatEntry 2 } + +--- +--- muninConformance +--- + +muninConformance OBJECT IDENTIFIER ::= { munin 101 } + +muninCompliances MODULE-COMPLIANCE + STATUS current + DESCRIPTION + "munin compliance statement" + MODULE -- this module + MANDATORY-GROUPS { + muninStatGroup + } + ::= { muninConformance 1 } + +muninStatGroup OBJECT-GROUP + OBJECTS { + muninStat + } + STATUS current + DESCRIPTION + "Conformance groupe for munin statistics." + ::= { muninConformance 2 } + +END diff --git a/tools/munin2snmp/munin2snmp b/tools/munin2snmp/munin2snmp new file mode 100755 index 00000000..fb36c9f8 --- /dev/null +++ b/tools/munin2snmp/munin2snmp @@ -0,0 +1,284 @@ +#!/usr/bin/perl + +use warnings; +use strict; +use NetSNMP::OID; +use NetSNMP::ASN (':all'); +use NetSNMP::agent (':all'); +use IO::Socket; +use Getopt::Long; +use Pod::Usage; + +my %cache = (); # Cache +my @cache_oids = (); # Keys, sorted +my $cache_updated = 0; +my $delimiter = ' = '; +my $conf = '/etc/munin2snmp.conf'; +my %config; +my $pidfile = '/var/run/munin2snmp.pid'; + +my $usage = "Usage: $0 [options] \ +Options: +--help : print help message\ +--host [host] : munin-node host, default localhost\ +--port [port] : munin-node port, default 4949\ +--base_oid [OID] : base oid, default .1.3.6.1.4.1.123456.100.1.1\ + Don't forget to update MUNIN-MIB OBJECT IDENTIFIER if you modify base_oid\ +--plugins [load,cpu,..] : comma separated list of munin-node plugins, default cpu,load,df\ +--pidfile [file path] : pidfile, default /var/run/munin2snmp.pid\ +"; + +sub read_conf { + return if ( ! -e $conf ); + open my $conf_fh,'<',$conf or die "Can't open the $conf, $!\n"; + while (<$conf_fh>) { + chomp; + my ($param, $val) = split(/\s*=\s*/); + $config{"$param"} = $val; + } +} + +# initialize +my %Munin; +if ( ! scalar @ARGV ) { + read_conf; +} +GetOptions ( + "help" => sub{pod2usage($usage)}, + "host=s" => \$config{munin_host}, + "port=s" => \$config{munin_port}, + "base_oid=s" => \$config{'base_oid'}, + "plugins=s" => \$config{'munin_plugins'}, + "pidfile=s" => \$pidfile, +); +$Munin{PORT} = $config{'munin_port'} || '4949'; +$Munin{HOST} = $config{'munin_host'} || 'localhost'; +my $oidbase = $config{'base_oid'} || '.1.3.6.1.4.1.123456.100.1.1'; +# See munin plugins dir for more plugins +my @munin_plugins = qw (cpu load df); +@munin_plugins = split(',',$config{'munin_plugins'}) if ( $config{'munin_plugins'}); + +open my $pidfd,'>',$pidfile or die "Can't open $pidfile, $!\n"; +print $pidfd $$; +close $pidfd; + +# Update cache +sub update_stats { + return if time() - $cache_updated < 30; + %cache = (); + + foreach my $plugin (@munin_plugins) { + $plugin =~ s/^.*\///; + my $DATA = munin_fetch($plugin); + foreach my $line (@$DATA) { + # Extract name and value + next if $line !~ m/^(.*)\s=\s(.*)$/xms; + my ($name,$value) = ($1,$2); + # Compute OID + my $oid = "$oidbase"; + foreach my $char (split //, $name) { + $oid .= "."; + $oid .= ord($char); + } + # Put in the cache + $cache{$oid} = $value; + } + } + @cache_oids = sort { new NetSNMP::OID($a) <=> new NetSNMP::OID($b) } (keys %cache); + $cache_updated = time(); +} + +# Handle request +sub handle_stats { + my ($handler, $registration_info, $request_info, $requests) = @_; + update_stats; # Maybe we should do this in a thread... + for (my $request = $requests; $request; $request = $request->next()) { + $SNMP::use_numeric = 1; + my $oid = $request->getOID(); + my $noid=SNMP::translateObj($oid); + if ($request_info->getMode() == MODE_GET) { + # For a GET request, we just check the cache + if (exists $cache{$noid}) { + $request->setValue(ASN_OCTET_STR, "$cache{$noid}"); + } + } + elsif ($request_info->getMode() == MODE_GETNEXT) { + # For a GETNEXT, we need to find a best match. This is the + # first match strictly superior to the requested OID. + my $bestoid = undef; + foreach my $currentoid (@cache_oids) { + $currentoid = new NetSNMP::OID($currentoid); + next if $currentoid <= $oid; + $bestoid = $currentoid; + last; + } + if (defined $bestoid) { + $SNMP::use_numeric = 1; + my $noid=SNMP::translateObj($bestoid); + $request->setOID($bestoid); + $request->setValue(ASN_OCTET_STR, "$cache{$noid}"); + } + } + } +} + +my $agent = new NetSNMP::agent( + 'Name' => "munin", + 'AgentX' => 1); + +# Register MIB +$agent->register("munin-stats", $oidbase, + \&handle_stats) or die "registration of handler failed!\n"; + +# Main loop +$SIG{'INT'} = \&shutdown; +$SIG{'QUIT'} = \&shutdown; +my $running = 1; +while ($running) { + $agent->agent_check_and_process(1); +} +$agent->shutdown(); + +sub shutdown { + # Shutdown requested + $running = 0; + unlink $pidfile if -e $pidfile; +} + +#muninwalk +my @collect; +sub munin_fetch { + my $SOCKET = IO::Socket::INET -> new ( PeerAddr => $Munin{HOST}, + PeerPort => $Munin{PORT}, + Proto => 'tcp', + Timeout => '10', + Type => SOCK_STREAM, + ) or die "Cannot create socket - $@\n"; + my $tmp = <$SOCKET>; + while (my $fetch = shift(@_)) { + my $obj = ''; + if ($fetch =~ /^(\w+)(\.)(\w+)$/ ) { + ($fetch,$obj) = ($1,$3); + } + $SOCKET->print("fetch $fetch\n"); + select($SOCKET); + select(STDOUT); + while (<$SOCKET> ) { + chomp; + $_ =~ s/(.value)//; + if ($_ =~ /^(.*)(\s)(.+)$/) { + if (($1 eq '# Unknown') or ($1 eq '# Bad')) { + push (@collect,$fetch.$delimiter.'NULL'); + } else { + if (!$obj or ($1 eq $obj)) { + push (@collect,$fetch.".".$1.$delimiter.$3); + } + } + } else { + last; + } + } + } + $SOCKET->print("quit\n"); + $SOCKET->close(); + return \@collect if @collect; +} + +=head1 NAME + +munin2snmp - SNMP Agent to query munin-node over snmp + +=head1 REQUIREMENTS + +Net::SNMP, Getopt::Long, Pod::Usage perl modules, munin-node with some plugins + +=head2 Example configuration + +/etc/snmp/snmpd.conf + + master agentx + agentAddress udp:127.0.0.1:161 + rocommunity public 127.0.0.1 + +On a newer system it is enough to define "master" option only + +MUNIN-MIB should be installed on the client, +it goes to /usr/local/share/snmp/mibs or /usr/share/munin/mibs +or another place where snmpd expects to find the MIB files. + +See also http://www.net-snmp.org/wiki/index.php/FAQ:MIBs_03 + +It is possible to start munin2snmp as non-root user, for example +run munin2snmp as Debian-snmp user on Debian Stretch: + +fix the /var/agentx permissions: + + chmod g+rx /var/agentx + chgrp Debian-snmp /var/agentx + +add to /etc/snmp/snmpd.conf: + + master agentx + agentXperms 0640 0550 Debian-snmp Debian-snmp + +restart snmpd and start the agent as Debian-snmp: + + su -l Debian-snmp -s /bin/bash -c "/tmp/munin2snmp.pl --pidfile /tmp/munin2snmp.pid --plugins iostat,vmstat" + +=head2 Usage + +After setting up snmpd, start the agent: + + ./munin2snmp + +Now one can query the agent + + snmpwalk -v 2c -mMUNIN-MIB -c public localhost .1.3.6.1.4.1.123456.100.1.1 + +where "1.3.6.1.4.1.123456.100.1.1" is example OID selected as the base +tree for the agent. + +Change OBJECT IDENTIFIER in the MUNIN-MIB file if you plan to use a different OID. + +You might need to change the host, port, oidbase and munin_plugins you want to use. + +The defaults: + + $Munin{PORT} = '4949'; + $Munin{HOST} = 'localhost' + $oidbase = ".1.3.6.1.4.1.123456.100.1.1" + @munin_plugins = qw ( load cpu df ); + +One can override the defaults by creating /etc/munin2snmp.conf file with the following +configuration options: + + munin_port = [port] + munin_host = [host] + base_oid = [oid] + munin_plugins = [comma separated list of munin-node plugins] + +Or by specifying the parameters, see munin2snmp --help for the usage + +=head1 ACKNOWLEDGEMENTS + +Heavily inspired by +Vincent Bernat: https://github.com/vincentbernat/extend-netsnmp +and Masahito Zembutsu: https://github.com/zembutsu/muninwalk + +=head1 LICENSE + +ISC License (ISC) + +Copyright (c) 2016, Alex Mestiashvili + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/tools/pypmmn/.gitignore b/tools/pypmmn/.gitignore deleted file mode 100644 index 0b7a733f..00000000 --- a/tools/pypmmn/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -env -build -MANIFEST -dist -plugins