diff --git a/html/includes/graphs/application/drbd_disk_bits.inc.php b/html/includes/graphs/application/drbd_disk_bits.inc.php
new file mode 100755
index 000000000..a91e8241e
--- /dev/null
+++ b/html/includes/graphs/application/drbd_disk_bits.inc.php
@@ -0,0 +1,21 @@
+
diff --git a/html/includes/graphs/application/drbd_network_bits.inc.php b/html/includes/graphs/application/drbd_network_bits.inc.php
new file mode 100755
index 000000000..133f87645
--- /dev/null
+++ b/html/includes/graphs/application/drbd_network_bits.inc.php
@@ -0,0 +1,21 @@
+
diff --git a/html/includes/graphs/application/drbd_queue.inc.php b/html/includes/graphs/application/drbd_queue.inc.php
new file mode 100644
index 000000000..08a90f8c3
--- /dev/null
+++ b/html/includes/graphs/application/drbd_queue.inc.php
@@ -0,0 +1,37 @@
+ 'Local I/O',
+ 'pe' => 'Pending',
+ 'ua' => 'UnAcked',
+ 'ap' => 'App Pending',
+);
+
+$i = 0;
+if (is_file($rrd_filename))
+{
+ foreach ($array as $ds => $vars)
+ {
+ $rrd_list[$i]['filename'] = $rrd_filename;
+ if (is_array($vars))
+ {
+ $rrd_list[$i]['descr'] = $vars['descr'];
+ } else {
+ $rrd_list[$i]['descr'] = $vars;
+ }
+ $rrd_list[$i]['ds'] = $ds;
+ $i++;
+ }
+} else { echo("file missing: $file"); }
+
+$colours = "mixed";
+$nototal = 0;
+$unit_text = "";
+
+include("includes/graphs/generic_multi_simplex_seperated.inc.php");
+
+?>
diff --git a/html/includes/graphs/application/drbd_unsynced.inc.php b/html/includes/graphs/application/drbd_unsynced.inc.php
new file mode 100755
index 000000000..03293e136
--- /dev/null
+++ b/html/includes/graphs/application/drbd_unsynced.inc.php
@@ -0,0 +1,28 @@
+
diff --git a/html/includes/graphs/graph.inc.php b/html/includes/graphs/graph.inc.php
index 1dd731838..eec34aed3 100644
--- a/html/includes/graphs/graph.inc.php
+++ b/html/includes/graphs/graph.inc.php
@@ -146,8 +146,10 @@ if ($error_msg) {
$dst_x = '0'; // destination x
$dst_y = '0'; // destination y
$dst_im = imagecreatetruecolor($src_w, $src_h);
+ imagesavealpha($dst_im, true);
$white = imagecolorallocate($dst_im, 255, 255, 255);
- imagefill($dst_im, 0, 0, $white);
+ $trans_colour = imagecolorallocatealpha($dst_im, 0, 0, 0, 127);
+ imagefill($dst_im, 0, 0, $trans_colour);
imagecopy($dst_im, $src_im, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h);
imagepng($dst_im);
imagedestroy($dst_im);
diff --git a/html/pages/device/apps/drbd.inc.php b/html/pages/device/apps/drbd.inc.php
new file mode 100644
index 000000000..3749ce22f
--- /dev/null
+++ b/html/pages/device/apps/drbd.inc.php
@@ -0,0 +1,29 @@
+'.$app['app_instance'].'');
+
+$graphs = array('drbd_network_bits' => 'Network Traffic',
+ 'drbd_disk_bits' => 'Disk Traffic',
+ 'drbd_unsynced' => 'Unsynced Data',
+ 'drbd_queue' => 'Queues');
+
+foreach ($graphs as $key => $text)
+{
+
+ $graph_array['height'] = "100";
+ $graph_array['width'] = "215";
+ $graph_array['to'] = $now;
+ $graph_array['id'] = $app['app_id'];
+ $graph_array['type'] = "application_".$key;
+
+ echo('
'.$text.'
');
+
+ echo("| ");
+
+ include("includes/print-quadgraphs.inc.php");
+
+ echo(" |
");
+}
+
+
+?>
diff --git a/includes/defaults.inc.php b/includes/defaults.inc.php
index 1fc707c26..0b31b6280 100644
--- a/includes/defaults.inc.php
+++ b/includes/defaults.inc.php
@@ -1,5 +1,24 @@
+ * @copyright (C) 2006 - 2012 Adam Armstrong
+ * @license http://gnu.org/copyleft/gpl.html GNU GPL
+ *
+ */
+
####
#### Please don't edit this file -- make changes to the configuration array in config.php
####
@@ -413,7 +432,6 @@ $config['poller_modules']['bgp-peers'] = 1;
$config['poller_modules']['junose-atm-vp'] = 1;
$config['poller_modules']['toner'] = 1;
$config['poller_modules']['ucd-diskio'] = 1;
-$config['poller_modules']['applications'] = 1;
$config['poller_modules']['wifi'] = 1;
$config['poller_modules']['ospf'] = 1;
$config['poller_modules']['cisco-ipsec-flow-monitor'] = 1;
@@ -427,6 +445,8 @@ $config['poller_modules']['cisco-ace-serverfarms'] = 1;
$config['poller_modules']['netscaler-vsvr'] = 1;
$config['poller_modules']['entity-physical'] = 1;
$config['poller_modules']['unix-agent'] = 0;
+$config['poller_modules']['applications'] = 1;
+
## List of discovery modules. Need to be in this array to be
## considered for execution.
diff --git a/includes/polling/applications/apache.inc.php b/includes/polling/applications/apache.inc.php
index 32aae6986..3dbd5eb71 100644
--- a/includes/polling/applications/apache.inc.php
+++ b/includes/polling/applications/apache.inc.php
@@ -2,16 +2,22 @@
## Polls Apache statistics from script via SNMP
+if(!empty($agent_data['apache']))
+{
+ $apache = $agent_data['apache'];
+} else {
+ $options = "-O qv";
+ $oid = "nsExtendOutputFull.6.97.112.97.99.104.101";
+ $apache = snmp_get($device, $oid, $options);
+}
$rrd_filename = $config['rrd_dir'] . "/" . $device['hostname'] . "/app-apache-".$app['app_id'].".rrd";
-$options = "-O qv";
-$oid = "nsExtendOutputFull.6.97.112.97.99.104.101";
-$apache = snmp_get($device, $oid, $options);
echo(" apache");
-list ($total_access, $total_kbyte, $cpuload, $uptime, $reqpersec, $bytespersec, $bytesperreq, $busyworkers, $idleworkers,
- $score_wait, $score_start, $score_reading, $score_writing, $score_keepalive, $score_dns, $score_closing, $score_logging, $score_graceful, $score_idle, $score_open) = explode("\n", $apache);
+ list ($total_access, $total_kbyte, $cpuload, $uptime, $reqpersec, $bytespersec, $bytesperreq, $busyworkers, $idleworkers,
+ $score_wait, $score_start, $score_reading, $score_writing, $score_keepalive, $score_dns, $score_closing, $score_logging,
+ $score_graceful, $score_idle, $score_open) = explode("\n", $apache);
if (!is_file($rrd_filename))
{
@@ -52,4 +58,4 @@ if (!is_file($rrd_filename))
rrdtool_update($rrd_filename, "N:$total_access:$total_kbyte:$cpuload:$uptime:$reqpersec:$bytespersec:$bytesperreq:$busyworkers:$idleworkers:$score_wait:$score_start:$score_reading:$score_writing:$score_keepalive:$score_dns:$score_closing:$score_logging:$score_graceful:$score_idle:$score_open");
-?>
\ No newline at end of file
+?>
diff --git a/includes/polling/applications/drbd.inc.php b/includes/polling/applications/drbd.inc.php
new file mode 100644
index 000000000..6ca722dbd
--- /dev/null
+++ b/includes/polling/applications/drbd.inc.php
@@ -0,0 +1,48 @@
+
diff --git a/includes/polling/applications/mysql.inc.php b/includes/polling/applications/mysql.inc.php
index df8457094..75b8e4ad1 100644
--- a/includes/polling/applications/mysql.inc.php
+++ b/includes/polling/applications/mysql.inc.php
@@ -1,14 +1,19 @@
"10" || count($pkgs_db[$manager][$name][$arch], 1) == '0') {
- dbInsert(array('device_id' => $device['device_id'], 'name' => $name, 'manager' => $manager,
- 'status' => 1, 'version' => $version, 'build' => $build, 'arch' => $arch, 'size' => $size), 'packages');
- echo("+".$name."-".$version."-".$build."-".$arch);
- log_event('Package installed: '.$name.' ('.$arch.') version '.$version.'-'.$build, $device, 'package');
- } elseif(count($pkgs_db[$manager][$name][$arch], 1)) {
- $pkg_c = dbFetchRow("SELECT * FROM `packages` WHERE `device_id` = ? AND `manager` = ? AND `name` = ? and `arch` = ? ORDER BY version DESC, build DESC", array($device['device_id'], $manager, $name, $arch));
- echo("U(".$pkg_c['name']."-".$pkg_c['version']."-".$pkg_c['build']."|".$name."-".$version."-".$build.")");
- $pkg_update = array('version' => $version, 'build' => $build, 'status' => '1', 'size' => $size);
- dbUpdate($pkg_update, 'packages', '`pkg_id` = ?', array($pkg_c['pkg_id']));
- log_event('Package updated: '.$name.' ('.$arch.') from '.$pkg_c['version'].'-'.$pkg_c['build'] .' to '.$version.'-'.$build, $device, 'package');
- unset($pkgs_db_id[$pkg_c['pkg_id']]);
- }
- }
- unset($pkg_update);
- }
}
+
+ ## This is run for all "packages" and is common to RPM/DEB/etc
+ foreach ($pkgs_id as $pkg) {
+ $name = $pkg['name'];
+ $version = $pkg['version'];
+ $build = $pkg['build'];
+ $arch = $pkg['arch'];
+ $size = $pkg['size'];
+
+ #echo(str_pad($name, 20)." ".str_pad($version, 10)." ".str_pad($build, 10)." ".$arch."\n");
+ #echo($name." ");
+
+ if (is_array($pkgs_db[$pkg['manager']][$pkg['name']][$pkg['arch']][$pkg['version']][$pkg['build']]))
+ {
+ ### FIXME - packages_history table
+ $id = $pkgs_db[$pkg['manager']][$pkg['name']][$pkg['arch']][$pkg['version']][$pkg['build']]['id'];
+ if ($pkgs_db[$pkg['manager']][$pkg['name']][$pkg['arch']][$pkg['version']][$pkg['build']]['status'] != '1')
+ {
+ $pkg_update['status'] = '1';
+ }
+ if ($pkgs_db[$pkg['manager']][$pkg['name']][$pkg['arch']][$pkg['version']][$pkg['build']]['size'] != $size)
+ {
+ $pkg_update['size'] = $size;
+ }
+ if (!empty($pkg_update))
+ {
+ dbUpdate($pkg_update, 'packages', '`pkg_id` = ?', array($id));
+ echo("u");
+ } else {
+ echo(".");
+ }
+ unset($pkgs_db_id[$id]);
+ } else {
+ if (count($pkgs[$manager][$name][$arch], 1) > "10" || count($pkgs_db[$manager][$name][$arch], 1) == '0') {
+ dbInsert(array('device_id' => $device['device_id'], 'name' => $name, 'manager' => $manager,
+ 'status' => 1, 'version' => $version, 'build' => $build, 'arch' => $arch, 'size' => $size), 'packages');
+ echo("+".$name."-".$version."-".$build."-".$arch);
+ log_event('Package installed: '.$name.' ('.$arch.') version '.$version.'-'.$build, $device, 'package');
+ } elseif(count($pkgs_db[$manager][$name][$arch], 1)) {
+ $pkg_c = dbFetchRow("SELECT * FROM `packages` WHERE `device_id` = ? AND `manager` = ? AND `name` = ? and `arch` = ? ORDER BY version DESC, build DESC", array($device['device_id'], $manager, $name, $arch));
+ echo("U(".$pkg_c['name']."-".$pkg_c['version']."-".$pkg_c['build']."|".$name."-".$version."-".$build.")");
+ $pkg_update = array('version' => $version, 'build' => $build, 'status' => '1', 'size' => $size);
+ dbUpdate($pkg_update, 'packages', '`pkg_id` = ?', array($pkg_c['pkg_id']));
+ log_event('Package updated: '.$name.' ('.$arch.') from '.$pkg_c['version'].'-'.$pkg_c['build'] .' to '.$version.'-'.$build, $device, 'package');
+ unset($pkgs_db_id[$pkg_c['pkg_id']]);
+ }
+ }
+ unset($pkg_update);
+ }
+
+ ## Packages
foreach ($pkgs_db_id as $id => $pkg)
{
dbDelete('packages', "`pkg_id` = ?", array($id));
@@ -117,6 +123,60 @@ if(!empty($agent_raw))
log_event('Package removed: '.$pkg['name'].' '.$pkg['arch'].' '.$pkg['version'].'-'.$pkg['build'], $device, 'package');
}
+ ### Processes
+ if (!empty($agent_data['ps']))
+ {
+ echo("\nProcesses: ");
+ foreach (explode("\n", $agent_data['ps']) as $process)
+ {
+ $process = preg_replace("/\((.*),([0-9]*),([0-9]*),([0-9\.]*)\)\ (.*)/", "\\1|\\2|\\3|\\4|\\5", $process);
+ list($user, $vsz, $rss, $pcpu, $command) = explode("|", $process, 5);
+ $processlist[] = array('user' => $user, 'vsz' => $vsz, 'rss' => $rss, 'pcpu' => $pcpu, 'command' => $command);
+ }
+ #print_r($processlist);
+ }
+
+ ### Apache
+ if (!empty($agent_data['apache']))
+ {
+ $app_found['apache'] = TRUE;
+ if(dbFetchCell("SELECT COUNT(*) FROM `applications` WHERE `device_id` = ? AND `app_type` = ?", array($device['device_id'], 'apache')) == "0")
+ {
+ echo("Found new application 'Apache'\n");
+ dbInsert(array('device_id' => $device['device_id'], 'app_type' => 'apache'), 'applications');
+ }
+ }
+
+ ### MySQL
+ if (!empty($agent_data['mysql']))
+ {
+ $app_found['mysql'] = TRUE;
+ if(dbFetchCell("SELECT COUNT(*) FROM `applications` WHERE `device_id` = ? AND `app_type` = ?", array($device['device_id'], 'mysql')) == "0")
+ {
+ echo("Found new application 'MySQL'\n");
+ dbInsert(array('device_id' => $device['device_id'], 'app_type' => 'mysql'), 'applications');
+ }
+ }
+
+ ### DRBd
+ if (!empty($agent_data['drbd']))
+ {
+ $agent_data['drbd_raw'] = $agent_data['drbd'];
+ $agent_data['drbd'] = array();
+ foreach (explode("\n", $agent_data['drbd_raw']) as $drbd_entry)
+ {
+ list($drbd_dev, $drbd_data) = explode(":", $drbd_entry);
+ if(preg_match("/^drbd/", $drbd_dev))
+ {
+ $agent_data['drbd'][$drbd_dev] = $drbd_data;
+ if(dbFetchCell("SELECT COUNT(*) FROM `applications` WHERE `device_id` = ? AND `app_type` = ? AND `app_instance` = ?", array($device['device_id'], 'drbd', $drbd_dev)) == "0")
+ {
+ echo("Found new application 'DRBd' $drbd_dev\n");
+ dbInsert(array('device_id' => $device['device_id'], 'app_type' => 'drbd', 'app_instance' => $drbd_dev), 'applications');
+ }
+ }
+ }
+ }
}
unset($pkg);
diff --git a/scripts/agent-local/drbd b/scripts/agent-local/drbd
new file mode 100755
index 000000000..49e62b1f5
--- /dev/null
+++ b/scripts/agent-local/drbd
@@ -0,0 +1,372 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+check_drbd - Nagios plugin for DRBD
+
+=head1 SYNOPSIS
+
+B [B<--verbose> | B<-v>]
+
+=head1 DESCRIPTION
+
+B is a Nagios plugin for DRBD. It checks the connection state,
+resource roles and disk states for every configured DRBD resource, and
+produces a WARNING or CRITICAL alert if anything is amiss. The states
+of both the local and remote sides of each connection are monitored.
+
+=head2 Nagios status information
+
+The status information emitted by this plugin is similar to the information
+in F:
+
+ drbd0: Connected Primary/Secondary UpToDate/UpToDate
+ | | | | | |
+ | | | | | Remote disk state
+ | | | | Local disk state
+ | | | Remote resource role
+ | | Local resource role
+ | Connection state
+ DRBD device
+
+If more than one device is present, and all devices are OK, the output is
+summarised:
+
+ drbd0: PriConUpT, drbd1: SecConUpT
+
+If any devices are not OK, the output contains their statuses in full.
+
+=head2 Nagios performance data
+
+Complete performance data is emitted for all configured DRBD resources:
+
+=over
+
+=item drbdI<*>_ns
+
+=item drbdI<*>_nr
+
+The volume of network data sent to and received from the peer, in kiB.
+
+=item drbdI<*>_dw
+
+=item drbdI<*>_dr
+
+The volume of network data written to and read from the local disk, in kiB.
+
+=item drbdI<*>_al
+
+The number of updates of the activity log area of the metadata.
+
+=item drbdI<*>_lo
+
+The number of open requests to the local I/O subsystem issued by DRBD.
+
+=item drbdI<*>_pe
+
+The number of requests sent to the peer but not yet been answered by the latter.
+
+=item drbdI<*>_ua
+
+The number of requests received by the peer but not yet been answered by the latter.
+
+=item drbdI<*>_ap
+
+The number of block I/O requests forwarded by DRBD, but not yet answered by DRBD.
+
+=item drbdI<*>_ep
+
+The number of epoch objects.
+
+=item drbdI<*>_oos
+
+The amount of storage currently out-of-sync, in kiB.
+
+=back
+
+=head1 OPTIONS
+
+=over
+
+=item B<-v>, B<--verbose>
+
+Increase the verbosity of the output messages. This disables the Nagios status
+information summarisation described above: all resources' statuses are printed
+in full.
+
+=back
+
+=head1 EXIT STATUS
+
+=over
+
+=item 0
+
+All resources are OK.
+
+=item 1
+
+Some resources are not OK, but do not need immediate attention.
+
+=item 2
+
+Some resources are not OK and need immediate attention.
+
+=item 3
+
+An error occurred while collecting the resources' statuses.
+
+=back
+
+=head1 FILES
+
+F
+
+=head1 SEE ALSO
+
+L
+
+=cut
+
+use strict;
+use warnings;
+
+use constant BASENAME => ($0 =~ m{.*/([^/]+)})[0] || 'check_drbd';
+
+use constant STATE_FILE => '/proc/drbd';
+
+use constant {
+ OK => 0,
+ WARNING => 1,
+ CRITICAL => 2,
+ UNKNOWN => 3,
+};
+
+use Getopt::Long;
+use IO::File;
+
+sub help;
+sub usage;
+
+sub perfdata;
+sub ok;
+sub warning;
+sub critical;
+sub unknown;
+
+sub get_state;
+
+$SIG{__DIE__} = sub {
+ die @_ if $^S;
+ print @_;
+ exit UNKNOWN;
+};
+
+my $verbose;
+
+Getopt::Long::Configure('bundling', 'no_ignore_case');
+GetOptions(
+ 'verbose|v+' => \$verbose,
+ 'help|?' => sub { help; exit 0 },
+ 'usage' => sub { usage; exit 0 },
+) and @ARGV == 0
+ or do { usage; exit UNKNOWN };
+
+my @state = get_state;
+my $status = OK;
+
+print "<<>>\n";
+
+foreach my $id (0 .. $#state) {
+ my $device = $state[$id]
+ or next;
+
+ # Assume CRITICAL by default
+
+ foreach (qw( cs )) {
+ $device->{"${_}_level"} = {
+ Connected => OK,
+ Unconfigured => OK,
+ StandAlone => WARNING,
+ SyncingAll => WARNING,
+ SyncingQuick => WARNING,
+ SyncSource => WARNING,
+ SyncTarget => WARNING,
+ VerifyS => WARNING,
+ VerifyT => WARNING,
+ Disconnecting => WARNING,
+ TearDown => WARNING,
+ StartingSyncS => WARNING,
+ StartingSyncT => WARNING,
+ WFSyncUUID => WARNING,
+ }->{$device->{$_}};
+ $device->{"${_}_level"} = CRITICAL unless defined $device->{"${_}_level"};
+
+ if ($device->{oos}) {
+ $device->{oos_level} = {
+ StartingSyncS => OK,
+ StartingSyncT => OK,
+ SyncSource => OK,
+ SyncTarget => OK,
+ PausedSyncS => OK,
+ PausedSyncT => OK,
+ }->{$device->{$_}};
+ $device->{oos_level} = CRITICAL unless defined $device->{oos_level};
+ }
+ }
+
+ foreach (qw( ro pro )) {
+ $device->{"${_}_level"} = {
+ Primary => OK,
+ Secondary => OK,
+ }->{$device->{$_}};
+ $device->{"${_}_level"} = CRITICAL unless defined $device->{"${_}_level"};
+ }
+
+ foreach (qw( ds pds )) {
+ $device->{"${_}_level"} = {
+ UpToDate => OK,
+ Consistent => OK,
+ Negotiating => WARNING,
+ Attaching => WARNING,
+ }->{$device->{$_}};
+ $device->{"${_}_level"} = CRITICAL unless defined $device->{"${_}_level"};
+ }
+
+ my @extra;
+ if ($device->{oos}) {
+ push @extra, sprintf '%d kiB out-of-sync', $device->{oos};
+ }
+ if ($device->{iof} !~ /^r.--(.(-)?)?$/) {
+ $device->{iof_level} = CRITICAL;
+ push @extra, sprintf 'I/O flags: %s', $device->{iof};
+ }
+ my $extra = @extra ? sprintf(' (%s)', join ', ', @extra) : '';
+
+ my $level = OK;
+ foreach (grep /_level$/, keys %$device) {
+ $level = $device->{$_} if $level < $device->{$_};
+ }
+ $status = $level if $status < $level;
+
+ $device->{level} = $level;
+ $device->{info} = sprintf 'drbd%d:cs=%s|ro=%s|pro=%s|ds=%s|pds=%s|extra=%s', $id, $device->{cs}, $device->{ro}, $device->{pro}, $device->{ds}, $device->{pds}, $extra;
+ $device->{short} = sprintf 'drbd%d: %0.3s%0.3s%0.3s%s', $id, $device->{ro}, $device->{cs}, $device->{ds}, $extra; # Role and connstate reversed, like old check_drbd
+
+ foreach (qw( ns nr dw dr al bm )) {
+ my $value = $device->{$_};
+ defined $value
+ or next;
+ perfdata "${_}=${value}";
+ }
+
+ foreach (qw( lo pe ua ap oos )) {
+ my $value = $device->{$_};
+ defined $value
+ or next;
+ perfdata "${_}=${value}";
+ }
+}
+
+@state
+ or critical 'No DRBD volumes present';
+
+if ($status) {
+ my $message = join ', ', map $_->{info}, grep { defined and $_->{level} } @state;
+ if ($status == WARNING) {
+ warning $message;
+ } else {
+ critical $message;
+ }
+} else {
+ my $message = join ', ', map { ($verbose || @state == 1) ? $_->{info} : $_->{short} } grep defined, @state;
+ ok $message;
+}
+
+die;
+
+###########################################################################
+
+sub help {
+ print <) {
+ if (m(^ \s* (\d+): \s* cs:(\w+) \s+ (?:ro|st):(\w+)/(\w+) \s+ ds:(\w+)/(\w+) \s+ \S+ \s+ (\S+))x) {
+ $device = $state[$1] = {
+ cs => $2,
+ ro => $3,
+ pro => $4,
+ ds => $5,
+ pds => $6,
+ iof => $7,
+ };
+ next;
+ };
+
+ $device or next;
+ $device->{$1} = $2 while /(\w+):(\S+)/g;
+ }
+
+ @state;
+}
diff --git a/scripts/agent-local/mysql b/scripts/agent-local/mysql
new file mode 100755
index 000000000..14d7f36a7
--- /dev/null
+++ b/scripts/agent-local/mysql
@@ -0,0 +1,1246 @@
+#!/usr/bin/php
+ true, # Do you want to check InnoDB statistics?
+ 'master' => true, # Do you want to check binary logging?
+ 'slave' => true, # Do you want to check slave status?
+ 'procs' => true, # Do you want to check SHOW PROCESSLIST?
+);
+
+$use_ss = FALSE; # Whether to use the script server or not
+$debug = FALSE; # Define whether you want debugging behavior.
+$debug_log = FALSE; # If $debug_log is a filename, it'll be used.
+
+# ============================================================================
+# You should not need to change anything below this line.
+# ============================================================================
+$version = "1.1.7";
+
+# ============================================================================
+# Include settings from an external config file (issue 39).
+# ============================================================================
+if (file_exists(__FILE__ . '.cnf' ) ) {
+ require(__FILE__ . '.cnf');
+}
+
+# Make this a happy little script even when there are errors.
+$no_http_headers = true;
+ini_set('implicit_flush', false); # No output, ever.
+if ($debug ) {
+ ini_set('display_errors', true);
+ ini_set('display_startup_errors', true);
+ ini_set('error_reporting', 2147483647);
+}
+else {
+ ini_set('error_reporting', E_ERROR);
+}
+ob_start(); # Catch all output such as notices of undefined array indexes.
+function error_handler($errno, $errstr, $errfile, $errline) {
+ print("$errstr at $errfile line $errline\n");
+ debug("$errstr at $errfile line $errline");
+}
+# ============================================================================
+# Set up the stuff we need to be called by the script server.
+# ============================================================================
+#if ($use_ss ) {
+# if (file_exists( dirname(__FILE__) . "/../include/global.php") ) {
+# # See issue 5 for the reasoning behind this.
+# debug("including " . dirname(__FILE__) . "/../include/global.php");
+# include_once(dirname(__FILE__) . "/../include/global.php");
+# }
+# elseif (file_exists( dirname(__FILE__) . "/../include/config.php" ) ) {
+# # Some Cacti installations don't have global.php.
+# debug("including " . dirname(__FILE__) . "/../include/config.php");
+# include_once(dirname(__FILE__) . "/../include/config.php");
+# }
+#}
+
+# ============================================================================
+# Make sure we can also be called as a script.
+# ============================================================================
+if (!isset($called_by_script_server)) {
+ debug($_SERVER["argv"]);
+ array_shift($_SERVER["argv"]); # Strip off this script's filename
+ $options = parse_cmdline($_SERVER["argv"]);
+ validate_options($options);
+ $result = ss_get_mysql_stats($options);
+
+ debug($result);
+ if (!$debug ) {
+ # Throw away the buffer, which ought to contain only errors.
+ ob_end_clean();
+ }
+ else {
+ ob_end_flush(); # In debugging mode, print out the errors.
+ }
+
+ echo("<<>>\n");
+
+ # Split the result up and extract only the desired parts of it.
+ $options['items'] = "";
+ $wanted = explode(',', $options['items']);
+ $output = array();
+ foreach ( explode(' ', $result) as $item ) {
+ if (in_array(substr($item, 0, 2), $wanted) ) {
+ $output[] = $item;
+ }
+ list($short, $val) = explode(":", $item);
+ echo(strtolower($short).":".strtolower($val)."\n");
+ }
+ debug(array("Final result", $output));
+ print(implode(' ', $output));
+}
+
+# ============================================================================
+# End "if file was not included" section.
+# ============================================================================
+}
+
+# ============================================================================
+# Work around the lack of array_change_key_case in older PHP.
+# ============================================================================
+if (!function_exists('array_change_key_case') ) {
+ function array_change_key_case($arr) {
+ $res = array();
+ foreach ( $arr as $key => $val ) {
+ $res[strtolower($key)] = $val;
+ }
+ return $res;
+ }
+}
+
+# ============================================================================
+# Validate that the command-line options are here and correct
+# ============================================================================
+function validate_options($options) {
+ debug($options);
+ $opts = array('items', 'user', 'pass', 'heartbeat', 'nocache', 'port');
+ # Required command-line options
+ foreach ( array() as $option ) {
+ if (!isset($options[$option]) || !$options[$option] ) {
+ usage("Required option --$option is missing");
+ }
+ }
+ foreach ( $options as $key => $val ) {
+ if (!in_array($key, $opts) ) {
+ usage("Unknown option --$key");
+ }
+ }
+}
+
+# ============================================================================
+# Print out a brief usage summary
+# ============================================================================
+function usage($message) {
+ global $mysql_host, $mysql_user, $mysql_pass, $mysql_port, $heartbeat;
+
+ $usage = << --items - [OPTION]
+
+ --host Hostname to connect to; use host:port syntax to specify a port
+ Use :/path/to/socket if you want to connect via a UNIX socket
+ --items Comma-separated list of the items whose data you want
+ --user MySQL username; defaults to $mysql_user if not given
+ --pass MySQL password; defaults to $mysql_pass if not given
+ --heartbeat MySQL heartbeat table; defaults to '$heartbeat' (see mk-heartbeat)
+ --nocache Do not cache results in a file
+ --port MySQL port; defaults to $mysql_port if not given
+ --mysql_ssl Add the MYSQL_CLIENT_SSL flag to mysql_connect() call
+
+EOF;
+ die($usage);
+}
+
+# ============================================================================
+# Parse command-line arguments, in the format --arg value --arg value, and
+# return them as an array ( arg => value )
+# ============================================================================
+function parse_cmdline( $args ) {
+ $result = array();
+ $cur_arg = '';
+ foreach ($args as $val) {
+ if (strpos($val, '--') === 0 ) {
+ if (strpos($val, '--no') === 0 ) {
+ # It's an option without an argument, but it's a --nosomething so
+ # it's OK.
+ $result[substr($val, 2)] = 1;
+ $cur_arg = '';
+ }
+ elseif ($cur_arg ) { # Maybe the last --arg was an option with no arg
+ if ($cur_arg == '--user' || $cur_arg == '--pass' || $cur_arg == '--port' ) {
+ # Special case because Cacti will pass these without an arg
+ $cur_arg = '';
+ }
+ else {
+ die("No arg: $cur_arg\n");
+ }
+ }
+ else {
+ $cur_arg = $val;
+ }
+ }
+ else {
+ $result[substr($cur_arg, 2)] = $val;
+ $cur_arg = '';
+ }
+ }
+ if ($cur_arg && ($cur_arg != '--user' && $cur_arg != '--pass' && $cur_arg != '--port') ) {
+ die("No arg: $cur_arg\n");
+ }
+ debug($result);
+ return $result;
+}
+
+# ============================================================================
+# This is the main function. Some parameters are filled in from defaults at the
+# top of this file.
+# ============================================================================
+function ss_get_mysql_stats( $options ) {
+ # Process connection options and connect to MySQL.
+ global $debug, $mysql_user, $mysql_pass, $heartbeat, $cache_dir, $poll_time,
+ $chk_options, $mysql_host, $mysql_port, $mysql_ssl;
+
+ # Connect to MySQL.
+ $user = isset($options['user']) ? $options['user'] : $mysql_user;
+ $pass = isset($options['pass']) ? $options['pass'] : $mysql_pass;
+ $port = isset($options['port']) ? $options['port'] : $mysql_port;
+ $host = isset($options['host']) ? $options['host'] : $mysql_host;
+
+ $heartbeat = isset($options['heartbeat']) ? $options['heartbeat'] : $heartbeat;
+ # If there is a port, or if it's a non-standard port, we add ":$port" to the
+ # hostname.
+ $host_str = $host
+ . $port != 3306 ? ":$port" : '';
+ debug(array('connecting to', $host_str, $user, $pass));
+ if (!extension_loaded('mysql') ) {
+ debug("The MySQL extension is not loaded");
+ die("The MySQL extension is not loaded");
+ }
+ if ($mysql_ssl || (isset($options['mysql_ssl']) && $options['mysql_ssl']) ) {
+ $conn = mysql_connect($host_str, $user, $pass, true, MYSQL_CLIENT_SSL);
+ }
+ else {
+ $conn = mysql_connect($host_str, $user, $pass);
+ }
+ if (!$conn ) {
+ die("MySQL: " . mysql_error());
+ }
+
+ $sanitized_host = str_replace(array(":", "/"), array("", "_"), $host);
+ $cache_file = "$cache_dir/$sanitized_host-mysql_cacti_stats.txt"
+ . $port != 3306 ? ":$port" : '';
+ debug("Cache file is $cache_file");
+
+ # First, check the cache.
+ $fp = null;
+ if (!isset($options['nocache']) ) {
+ if ($fp = fopen($cache_file, 'a+') ) {
+ $locked = flock($fp, 1); # LOCK_SH
+ if ($locked ) {
+ if (filesize($cache_file) > 0
+ && filectime($cache_file) + ($poll_time/2) > time()
+ && ($arr = file($cache_file))
+ ) {# The cache file is good to use.
+ debug("Using the cache file");
+ fclose($fp);
+ return $arr[0];
+ }
+ else {
+ debug("The cache file seems too small or stale");
+ # Escalate the lock to exclusive, so we can write to it.
+ if (flock($fp, 2) ) { # LOCK_EX
+ # We might have blocked while waiting for that LOCK_EX, and
+ # another process ran and updated it. Let's see if we can just
+ # return the data now:
+ if (filesize($cache_file) > 0
+ && filectime($cache_file) + ($poll_time/2) > time()
+ && ($arr = file($cache_file))
+ ) {# The cache file is good to use.
+ debug("Using the cache file");
+ fclose($fp);
+ return $arr[0];
+ }
+ ftruncate($fp, 0); # Now it's ready for writing later.
+ }
+ }
+ }
+ else {
+ debug("Couldn't lock the cache file, ignoring it.");
+ $fp = null;
+ }
+ }
+ }
+ else {
+ $fp = null;
+ debug("Couldn't open the cache file");
+ }
+
+ # Set up variables.
+ $status = array( # Holds the result of SHOW STATUS, SHOW INNODB STATUS, etc
+ # Define some indexes so they don't cause errors with += operations.
+ 'relay_log_space' => null,
+ 'binary_log_space' => null,
+ 'current_transactions' => null,
+ 'locked_transactions' => null,
+ 'active_transactions' => null,
+ 'innodb_locked_tables' => null,
+ 'innodb_tables_in_use' => null,
+ 'innodb_lock_structs' => null,
+ 'innodb_lock_wait_secs' => null,
+ 'innodb_sem_waits' => null,
+ 'innodb_sem_wait_time_ms'=> null,
+ # Values for the 'state' column from SHOW PROCESSLIST (converted to
+ # lowercase, with spaces replaced by underscores)
+ 'State_closing_tables' => null,
+ 'State_copying_to_tmp_table' => null,
+ 'State_end' => null,
+ 'State_freeing_items' => null,
+ 'State_init' => null,
+ 'State_locked' => null,
+ 'State_login' => null,
+ 'State_preparing' => null,
+ 'State_reading_from_net' => null,
+ 'State_sending_data' => null,
+ 'State_sorting_result' => null,
+ 'State_statistics' => null,
+ 'State_updating' => null,
+ 'State_writing_to_net' => null,
+ 'State_none' => null,
+ 'State_other' => null, # Everything not listed above
+ );
+
+ # Get SHOW STATUS and convert the name-value array into a simple
+ # associative array.
+ $result = run_query("SHOW /*!50002 GLOBAL */ STATUS", $conn);
+ foreach ( $result as $row ) {
+ $status[$row[0]] = $row[1];
+ }
+
+ # Get SHOW VARIABLES and do the same thing, adding it to the $status array.
+ $result = run_query("SHOW VARIABLES", $conn);
+ foreach ( $result as $row ) {
+ $status[$row[0]] = $row[1];
+ }
+
+ # Get SHOW SLAVE STATUS, and add it to the $status array.
+ if ($chk_options['slave'] ) {
+ $result = run_query("SHOW SLAVE STATUS", $conn);
+ $slave_status_rows_gotten = 0;
+ foreach ( $result as $row ) {
+ $slave_status_rows_gotten++;
+ # Must lowercase keys because different MySQL versions have different
+ # lettercase.
+ $row = array_change_key_case($row, CASE_LOWER);
+ $status['relay_log_space'] = $row['relay_log_space'];
+ $status['slave_lag'] = $row['seconds_behind_master'];
+
+ # Check replication heartbeat, if present.
+ if ($heartbeat ) {
+ $result2 = run_query(
+ "SELECT GREATEST(0, UNIX_TIMESTAMP() - UNIX_TIMESTAMP(ts) - 1)"
+ . " AS delay FROM $heartbeat WHERE id = 1", $conn);
+ $slave_delay_rows_gotten = 0;
+ foreach ( $result2 as $row2 ) {
+ $slave_delay_rows_gotten++;
+ if ($row2 && is_array($row2)
+ && array_key_exists('delay', $row2) )
+ {
+ $status['slave_lag'] = $row2['delay'];
+ }
+ else {
+ debug("Couldn't get slave lag from $heartbeat");
+ }
+ }
+ if ($slave_delay_rows_gotten == 0 ) {
+ debug("Got nothing from heartbeat query");
+ }
+ }
+
+ # Scale slave_running and slave_stopped relative to the slave lag.
+ $status['slave_running'] = ($row['slave_sql_running'] == 'Yes')
+ ? $status['slave_lag'] : 0;
+ $status['slave_stopped'] = ($row['slave_sql_running'] == 'Yes')
+ ? 0 : $status['slave_lag'];
+ }
+ if ($slave_status_rows_gotten == 0 ) {
+ debug("Got nothing from SHOW SLAVE STATUS");
+ }
+ }
+
+ # Get SHOW MASTER STATUS, and add it to the $status array.
+ if ($chk_options['master']
+ && array_key_exists('log_bin', $status)
+ && $status['log_bin'] == 'ON'
+ ) { # See issue #8
+ $binlogs = array(0);
+ $result = run_query("SHOW MASTER LOGS", $conn);
+ foreach ( $result as $row ) {
+ $row = array_change_key_case($row, CASE_LOWER);
+ # Older versions of MySQL may not have the File_size column in the
+ # results of the command. Zero-size files indicate the user is
+ # deleting binlogs manually from disk (bad user! bad!).
+ if (array_key_exists('file_size', $row) && $row['file_size'] > 0 ) {
+ $binlogs[] = $row['file_size'];
+ }
+ }
+ if (count($binlogs)) {
+ $status['binary_log_space'] = to_int(array_sum($binlogs));
+ }
+ }
+
+ # Get SHOW PROCESSLIST and aggregate it by state, then add it to the array
+ # too.
+ if ($chk_options['procs'] ) {
+ $result = run_query('SHOW PROCESSLIST', $conn);
+ foreach ( $result as $row ) {
+ $state = $row['State'];
+ if (is_null($state) ) {
+ $state = 'NULL';
+ }
+ if ($state == '' ) {
+ $state = 'none';
+ }
+ $state = str_replace(' ', '_', strtolower($state));
+ if (array_key_exists("State_$state", $status) ) {
+ increment($status, "State_$state", 1);
+ }
+ else {
+ increment($status, "State_other", 1);
+ }
+ }
+ }
+
+ # Get SHOW INNODB STATUS and extract the desired metrics from it, then add
+ # those to the array too.
+ if ($chk_options['innodb']
+ && array_key_exists('have_innodb', $status)
+ && $status['have_innodb'] == 'YES'
+ ) {
+ $result = run_query("SHOW /*!50000 ENGINE*/ INNODB STATUS", $conn);
+ $istatus_text = $result[0]['Status'];
+ $istatus_vals = get_innodb_array($istatus_text);
+
+ # Override values from InnoDB parsing with values from SHOW STATUS,
+ # because InnoDB status might not have everything and the SHOW STATUS is
+ # to be preferred where possible.
+ $overrides = array(
+ 'Innodb_buffer_pool_pages_data' => 'database_pages',
+ 'Innodb_buffer_pool_pages_dirty' => 'modified_pages',
+ 'Innodb_buffer_pool_pages_free' => 'free_pages',
+ 'Innodb_buffer_pool_pages_total' => 'pool_size',
+ 'Innodb_data_fsyncs' => 'file_fsyncs',
+ 'Innodb_data_pending_reads' => 'pending_normal_aio_reads',
+ 'Innodb_data_pending_writes' => 'pending_normal_aio_writes',
+ 'Innodb_os_log_pending_fsyncs' => 'pending_log_flushes',
+ 'Innodb_pages_created' => 'pages_created',
+ 'Innodb_pages_read' => 'pages_read',
+ 'Innodb_pages_written' => 'pages_written',
+ 'Innodb_rows_deleted' => 'rows_deleted',
+ 'Innodb_rows_inserted' => 'rows_inserted',
+ 'Innodb_rows_read' => 'rows_read',
+ 'Innodb_rows_updated' => 'rows_updated',
+ );
+
+ # If the SHOW STATUS value exists, override...
+ foreach ( $overrides as $key => $val ) {
+ if (array_key_exists($key, $status) ) {
+ debug("Override $key");
+ $istatus_vals[$val] = $status[$key];
+ }
+ }
+
+ # Now copy the values into $status.
+ foreach ( $istatus_vals as $key => $val ) {
+ $status[$key] = $istatus_vals[$key];
+ }
+ }
+
+ # Make table_open_cache backwards-compatible (issue 63).
+ if (array_key_exists('table_open_cache', $status) ) {
+ $status['table_cache'] = $status['table_open_cache'];
+ }
+
+ # Compute how much of the key buffer is used and unflushed (issue 127).
+ $status['Key_buf_bytes_used']
+ = big_sub($status['key_buffer_size'],
+ big_multiply($status['Key_blocks_unused'],
+ $status['key_cache_block_size']));
+ $status['Key_buf_bytes_unflushed']
+ = big_multiply($status['Key_blocks_not_flushed'],
+ $status['key_cache_block_size']);
+
+ if (array_key_exists('unflushed_log', $status)
+ && $status['unflushed_log']
+ ) {
+ # TODO: I'm not sure what the deal is here; need to debug this. But the
+ # unflushed log bytes spikes a lot sometimes and it's impossible for it to
+ # be more than the log buffer.
+ debug("Unflushed log: $status[unflushed_log]");
+ $status['unflushed_log']
+ = max($status['unflushed_log'], $status['innodb_log_buffer_size']);
+ }
+
+ # Define the variables to output. I use shortened variable names so maybe
+ # it'll all fit in 1024 bytes for Cactid and Spine's benefit. This list must
+ # come right after the word MAGIC_VARS_DEFINITIONS. The Perl script parses
+ # it and uses it as a Perl variable.
+ $keys = array(
+ 'Key_read_requests' => 'a0',
+ 'Key_reads' => 'a1',
+ 'Key_write_requests' => 'a2',
+ 'Key_writes' => 'a3',
+ 'history_list' => 'a4',
+ 'innodb_transactions' => 'a5',
+ 'read_views' => 'a6',
+ 'current_transactions' => 'a7',
+ 'locked_transactions' => 'a8',
+ 'active_transactions' => 'a9',
+ 'pool_size' => 'aa',
+ 'free_pages' => 'ab',
+ 'database_pages' => 'ac',
+ 'modified_pages' => 'ad',
+ 'pages_read' => 'ae',
+ 'pages_created' => 'af',
+ 'pages_written' => 'ag',
+ 'file_fsyncs' => 'ah',
+ 'file_reads' => 'ai',
+ 'file_writes' => 'aj',
+ 'log_writes' => 'ak',
+ 'pending_aio_log_ios' => 'al',
+ 'pending_aio_sync_ios' => 'am',
+ 'pending_buf_pool_flushes' => 'an',
+ 'pending_chkp_writes' => 'ao',
+ 'pending_ibuf_aio_reads' => 'ap',
+ 'pending_log_flushes' => 'aq',
+ 'pending_log_writes' => 'ar',
+ 'pending_normal_aio_reads' => 'as',
+ 'pending_normal_aio_writes' => 'at',
+ 'ibuf_inserts' => 'au',
+ 'ibuf_merged' => 'av',
+ 'ibuf_merges' => 'aw',
+ 'spin_waits' => 'ax',
+ 'spin_rounds' => 'ay',
+ 'os_waits' => 'az',
+ 'rows_inserted' => 'b0',
+ 'rows_updated' => 'b1',
+ 'rows_deleted' => 'b2',
+ 'rows_read' => 'b3',
+ 'Table_locks_waited' => 'b4',
+ 'Table_locks_immediate' => 'b5',
+ 'Slow_queries' => 'b6',
+ 'Open_files' => 'b7',
+ 'Open_tables' => 'b8',
+ 'Opened_tables' => 'b9',
+ 'innodb_open_files' => 'ba',
+ 'open_files_limit' => 'bb',
+ 'table_cache' => 'bc',
+ 'Aborted_clients' => 'bd',
+ 'Aborted_connects' => 'be',
+ 'Max_used_connections' => 'bf',
+ 'Slow_launch_threads' => 'bg',
+ 'Threads_cached' => 'bh',
+ 'Threads_connected' => 'bi',
+ 'Threads_created' => 'bj',
+ 'Threads_running' => 'bk',
+ 'max_connections' => 'bl',
+ 'thread_cache_size' => 'bm',
+ 'Connections' => 'bn',
+ 'slave_running' => 'bo',
+ 'slave_stopped' => 'bp',
+ 'Slave_retried_transactions' => 'bq',
+ 'slave_lag' => 'br',
+ 'Slave_open_temp_tables' => 'bs',
+ 'Qcache_free_blocks' => 'bt',
+ 'Qcache_free_memory' => 'bu',
+ 'Qcache_hits' => 'bv',
+ 'Qcache_inserts' => 'bw',
+ 'Qcache_lowmem_prunes' => 'bx',
+ 'Qcache_not_cached' => 'by',
+ 'Qcache_queries_in_cache' => 'bz',
+ 'Qcache_total_blocks' => 'c0',
+ 'query_cache_size' => 'c1',
+ 'Questions' => 'c2',
+ 'Com_update' => 'c3',
+ 'Com_insert' => 'c4',
+ 'Com_select' => 'c5',
+ 'Com_delete' => 'c6',
+ 'Com_replace' => 'c7',
+ 'Com_load' => 'c8',
+ 'Com_update_multi' => 'c9',
+ 'Com_insert_select' => 'ca',
+ 'Com_delete_multi' => 'cb',
+ 'Com_replace_select' => 'cc',
+ 'Select_full_join' => 'cd',
+ 'Select_full_range_join' => 'ce',
+ 'Select_range' => 'cf',
+ 'Select_range_check' => 'cg',
+ 'Select_scan' => 'ch',
+ 'Sort_merge_passes' => 'ci',
+ 'Sort_range' => 'cj',
+ 'Sort_rows' => 'ck',
+ 'Sort_scan' => 'cl',
+ 'Created_tmp_tables' => 'cm',
+ 'Created_tmp_disk_tables' => 'cn',
+ 'Created_tmp_files' => 'co',
+ 'Bytes_sent' => 'cp',
+ 'Bytes_received' => 'cq',
+ 'innodb_log_buffer_size' => 'cr',
+ 'unflushed_log' => 'cs',
+ 'log_bytes_flushed' => 'ct',
+ 'log_bytes_written' => 'cu',
+ 'relay_log_space' => 'cv',
+ 'binlog_cache_size' => 'cw',
+ 'Binlog_cache_disk_use' => 'cx',
+ 'Binlog_cache_use' => 'cy',
+ 'binary_log_space' => 'cz',
+ 'innodb_locked_tables' => 'd0',
+ 'innodb_lock_structs' => 'd1',
+ 'State_closing_tables' => 'd2',
+ 'State_copying_to_tmp_table' => 'd3',
+ 'State_end' => 'd4',
+ 'State_freeing_items' => 'd5',
+ 'State_init' => 'd6',
+ 'State_locked' => 'd7',
+ 'State_login' => 'd8',
+ 'State_preparing' => 'd9',
+ 'State_reading_from_net' => 'da',
+ 'State_sending_data' => 'db',
+ 'State_sorting_result' => 'dc',
+ 'State_statistics' => 'dd',
+ 'State_updating' => 'de',
+ 'State_writing_to_net' => 'df',
+ 'State_none' => 'dg',
+ 'State_other' => 'dh',
+ 'Handler_commit' => 'di',
+ 'Handler_delete' => 'dj',
+ 'Handler_discover' => 'dk',
+ 'Handler_prepare' => 'dl',
+ 'Handler_read_first' => 'dm',
+ 'Handler_read_key' => 'dn',
+ 'Handler_read_next' => 'do',
+ 'Handler_read_prev' => 'dp',
+ 'Handler_read_rnd' => 'dq',
+ 'Handler_read_rnd_next' => 'dr',
+ 'Handler_rollback' => 'ds',
+ 'Handler_savepoint' => 'dt',
+ 'Handler_savepoint_rollback' => 'du',
+ 'Handler_update' => 'dv',
+ 'Handler_write' => 'dw',
+ # Some InnoDB stats added later...
+ 'innodb_tables_in_use' => 'dx',
+ 'innodb_lock_wait_secs' => 'dy',
+ 'hash_index_cells_total' => 'dz',
+ 'hash_index_cells_used' => 'e0',
+ 'total_mem_alloc' => 'e1',
+ 'additional_pool_alloc' => 'e2',
+ 'uncheckpointed_bytes' => 'e3',
+ 'ibuf_used_cells' => 'e4',
+ 'ibuf_free_cells' => 'e5',
+ 'ibuf_cell_count' => 'e6',
+ 'adaptive_hash_memory' => 'e7',
+ 'page_hash_memory' => 'e8',
+ 'dictionary_cache_memory' => 'e9',
+ 'file_system_memory' => 'ea',
+ 'lock_system_memory' => 'eb',
+ 'recovery_system_memory' => 'ec',
+ 'thread_hash_memory' => 'ed',
+ 'innodb_sem_waits' => 'ee',
+ 'innodb_sem_wait_time_ms' => 'ef',
+ 'Key_buf_bytes_unflushed' => 'eg',
+ 'Key_buf_bytes_used' => 'eh',
+ 'key_buffer_size' => 'ei',
+ 'Innodb_row_lock_time' => 'ej',
+ 'Innodb_row_lock_waits' => 'ek',
+ );
+
+ # Return the output.
+ $output = array();
+ foreach ($keys as $key => $short ) {
+ # If the value isn't defined, return -1 which is lower than (most graphs')
+ # minimum value of 0, so it'll be regarded as a missing value.
+ $val = isset($status[$key]) ? $status[$key] : -1;
+ $output[] = "$short:$val";
+ }
+ $result = implode(' ', $output);
+ if ($fp ) {
+ if (fwrite($fp, $result) === FALSE ) {
+ die("Can't write '$cache_file'");
+ }
+ fclose($fp);
+ }
+
+ return $result;
+
+}
+
+# ============================================================================
+# Given INNODB STATUS text, returns a key-value array of the parsed text. Each
+# line shows a sample of the input for both standard InnoDB as you would find in
+# MySQL 5.0, and XtraDB or enhanced InnoDB from Percona if applicable. Note
+# that extra leading spaces are ignored due to trim().
+# ============================================================================
+function get_innodb_array($text) {
+ $results = array(
+ 'spin_waits' => array(),
+ 'spin_rounds' => array(),
+ 'os_waits' => array(),
+ 'pending_normal_aio_reads' => null,
+ 'pending_normal_aio_writes' => null,
+ 'pending_ibuf_aio_reads' => null,
+ 'pending_aio_log_ios' => null,
+ 'pending_aio_sync_ios' => null,
+ 'pending_log_flushes' => null,
+ 'pending_buf_pool_flushes' => null,
+ 'file_reads' => null,
+ 'file_writes' => null,
+ 'file_fsyncs' => null,
+ 'ibuf_inserts' => null,
+ 'ibuf_merged' => null,
+ 'ibuf_merges' => null,
+ 'log_bytes_written' => null,
+ 'unflushed_log' => null,
+ 'log_bytes_flushed' => null,
+ 'pending_log_writes' => null,
+ 'pending_chkp_writes' => null,
+ 'log_writes' => null,
+ 'pool_size' => null,
+ 'free_pages' => null,
+ 'database_pages' => null,
+ 'modified_pages' => null,
+ 'pages_read' => null,
+ 'pages_created' => null,
+ 'pages_written' => null,
+ 'queries_inside' => null,
+ 'queries_queued' => null,
+ 'read_views' => null,
+ 'rows_inserted' => null,
+ 'rows_updated' => null,
+ 'rows_deleted' => null,
+ 'rows_read' => null,
+ 'innodb_transactions' => null,
+ 'unpurged_txns' => null,
+ 'history_list' => null,
+ 'current_transactions' => null,
+ 'hash_index_cells_total' => null,
+ 'hash_index_cells_used' => null,
+ 'total_mem_alloc' => null,
+ 'additional_pool_alloc' => null,
+ 'last_checkpoint' => null,
+ 'uncheckpointed_bytes' => null,
+ 'ibuf_used_cells' => null,
+ 'ibuf_free_cells' => null,
+ 'ibuf_cell_count' => null,
+ 'adaptive_hash_memory' => null,
+ 'page_hash_memory' => null,
+ 'dictionary_cache_memory' => null,
+ 'file_system_memory' => null,
+ 'lock_system_memory' => null,
+ 'recovery_system_memory' => null,
+ 'thread_hash_memory' => null,
+ 'innodb_sem_waits' => null,
+ 'innodb_sem_wait_time_ms' => null,
+ );
+ $txn_seen = FALSE;
+ foreach ( explode("\n", $text) as $line ) {
+ $line = trim($line);
+ $row = preg_split('/ +/', $line);
+
+ # SEMAPHORES
+ if (strpos($line, 'Mutex spin waits') === 0 ) {
+ # Mutex spin waits 79626940, rounds 157459864, OS waits 698719
+ # Mutex spin waits 0, rounds 247280272495, OS waits 316513438
+ $results['spin_waits'][] = to_int($row[3]);
+ $results['spin_rounds'][] = to_int($row[5]);
+ $results['os_waits'][] = to_int($row[8]);
+ }
+ elseif (strpos($line, 'RW-shared spins') === 0 ) {
+ # RW-shared spins 3859028, OS waits 2100750; RW-excl spins 4641946, OS waits 1530310
+ $results['spin_waits'][] = to_int($row[2]);
+ $results['spin_waits'][] = to_int($row[8]);
+ $results['os_waits'][] = to_int($row[5]);
+ $results['os_waits'][] = to_int($row[11]);
+ }
+ elseif (strpos($line, 'seconds the semaphore:') > 0) {
+ # --Thread 907205 has waited at handler/ha_innodb.cc line 7156 for 1.00 seconds the semaphore:
+ increment($results, 'innodb_sem_waits', 1);
+ increment($results,
+ 'innodb_sem_wait_time_ms', to_int($row[9]) * 1000);
+ }
+
+ # TRANSACTIONS
+ elseif (strpos($line, 'Trx id counter') === 0 ) {
+ # The beginning of the TRANSACTIONS section: start counting
+ # transactions
+ # Trx id counter 0 1170664159
+ # Trx id counter 861B144C
+ $results['innodb_transactions'] = make_bigint($row[3], $row[4]);
+ $txn_seen = TRUE;
+ }
+ elseif (strpos($line, 'Purge done for trx') === 0 ) {
+ # Purge done for trx's n:o < 0 1170663853 undo n:o < 0 0
+ # Purge done for trx's n:o < 861B135D undo n:o < 0
+ $purged_to = make_bigint($row[6], $row[7] == 'undo' ? null : $row[7]);
+ $results['unpurged_txns']
+ = big_sub($results['innodb_transactions'], $purged_to);
+ }
+ elseif (strpos($line, 'History list length') === 0 ) {
+ # History list length 132
+ $results['history_list'] = to_int($row[3]);
+ }
+ elseif ($txn_seen && strpos($line, '---TRANSACTION') === 0 ) {
+ # ---TRANSACTION 0, not started, process no 13510, OS thread id 1170446656
+ increment($results, 'current_transactions', 1);
+ if (strpos($line, 'ACTIVE') > 0 ) {
+ increment($results, 'active_transactions', 1);
+ }
+ }
+ elseif ($txn_seen && strpos($line, '------- TRX HAS BEEN') === 0 ) {
+ # ------- TRX HAS BEEN WAITING 32 SEC FOR THIS LOCK TO BE GRANTED:
+ increment($results, 'innodb_lock_wait_secs', to_int($row[5]));
+ }
+ elseif (strpos($line, 'read views open inside InnoDB') > 0 ) {
+ # 1 read views open inside InnoDB
+ $results['read_views'] = to_int($row[0]);
+ }
+ elseif (strpos($line, 'mysql tables in use') === 0 ) {
+ # mysql tables in use 2, locked 2
+ increment($results, 'innodb_tables_in_use', to_int($row[4]));
+ increment($results, 'innodb_locked_tables', to_int($row[6]));
+ }
+ elseif ($txn_seen && strpos($line, 'lock struct(s)') > 0 ) {
+ # 23 lock struct(s), heap size 3024, undo log entries 27
+ # LOCK WAIT 12 lock struct(s), heap size 3024, undo log entries 5
+ # LOCK WAIT 2 lock struct(s), heap size 368
+ if (strpos($line, 'LOCK WAIT') === 0 ) {
+ increment($results, 'innodb_lock_structs', to_int($row[2]));
+ increment($results, 'locked_transactions', 1);
+ }
+ else {
+ increment($results, 'innodb_lock_structs', to_int($row[0]));
+ }
+ }
+
+ # FILE I/O
+ elseif (strpos($line, ' OS file reads, ') > 0 ) {
+ # 8782182 OS file reads, 15635445 OS file writes, 947800 OS fsyncs
+ $results['file_reads'] = to_int($row[0]);
+ $results['file_writes'] = to_int($row[4]);
+ $results['file_fsyncs'] = to_int($row[8]);
+ }
+ elseif (strpos($line, 'Pending normal aio reads:') === 0 ) {
+ # Pending normal aio reads: 0, aio writes: 0,
+ $results['pending_normal_aio_reads'] = to_int($row[4]);
+ $results['pending_normal_aio_writes'] = to_int($row[7]);
+ }
+ elseif (strpos($line, 'ibuf aio reads') === 0 ) {
+ # ibuf aio reads: 0, log i/o's: 0, sync i/o's: 0
+ $results['pending_ibuf_aio_reads'] = to_int($row[3]);
+ $results['pending_aio_log_ios'] = to_int($row[6]);
+ $results['pending_aio_sync_ios'] = to_int($row[9]);
+ }
+ elseif (strpos($line, 'Pending flushes (fsync)') === 0 ) {
+ # Pending flushes (fsync) log: 0; buffer pool: 0
+ $results['pending_log_flushes'] = to_int($row[4]);
+ $results['pending_buf_pool_flushes'] = to_int($row[7]);
+ }
+
+ # INSERT BUFFER AND ADAPTIVE HASH INDEX
+ elseif (strpos($line, 'Ibuf for space 0: size ') === 0 ) {
+ # Older InnoDB code seemed to be ready for an ibuf per tablespace. It
+ # had two lines in the output. Newer has just one line, see below.
+ # Ibuf for space 0: size 1, free list len 887, seg size 889, is not empty
+ # Ibuf for space 0: size 1, free list len 887, seg size 889,
+ $results['ibuf_used_cells'] = to_int($row[5]);
+ $results['ibuf_free_cells'] = to_int($row[9]);
+ $results['ibuf_cell_count'] = to_int($row[12]);
+ }
+ elseif (strpos($line, 'Ibuf: size ') === 0 ) {
+ # Ibuf: size 1, free list len 4634, seg size 4636,
+ $results['ibuf_used_cells'] = to_int($row[2]);
+ $results['ibuf_free_cells'] = to_int($row[6]);
+ $results['ibuf_cell_count'] = to_int($row[9]);
+ }
+ elseif (strpos($line, ' merged recs, ') > 0 ) {
+ # 19817685 inserts, 19817684 merged recs, 3552620 merges
+ $results['ibuf_inserts'] = to_int($row[0]);
+ $results['ibuf_merged'] = to_int($row[2]);
+ $results['ibuf_merges'] = to_int($row[5]);
+ }
+ elseif (strpos($line, 'Hash table size ') === 0 ) {
+ # In some versions of InnoDB, the used cells is omitted.
+ # Hash table size 4425293, used cells 4229064, ....
+ # Hash table size 57374437, node heap has 72964 buffer(s) <-- no used cells
+ $results['hash_index_cells_total'] = to_int($row[3]);
+ $results['hash_index_cells_used']
+ = strpos($line, 'used cells') > 0 ? to_int($row[6]) : '0';
+ }
+
+ # LOG
+ elseif (strpos($line, " log i/o's done, ") > 0 ) {
+ # 3430041 log i/o's done, 17.44 log i/o's/second
+ # 520835887 log i/o's done, 17.28 log i/o's/second, 518724686 syncs, 2980893 checkpoints
+ # TODO: graph syncs and checkpoints
+ $results['log_writes'] = to_int($row[0]);
+ }
+ elseif (strpos($line, " pending log writes, ") > 0 ) {
+ # 0 pending log writes, 0 pending chkp writes
+ $results['pending_log_writes'] = to_int($row[0]);
+ $results['pending_chkp_writes'] = to_int($row[4]);
+ }
+ elseif (strpos($line, "Log sequence number") === 0 ) {
+ # This number is NOT printed in hex in InnoDB plugin.
+ # Log sequence number 13093949495856 //plugin
+ # Log sequence number 125 3934414864 //normal
+ $results['log_bytes_written']
+ = isset($row[4])
+ ? make_bigint($row[3], $row[4])
+ : to_int($row[3]);
+ }
+ elseif (strpos($line, "Log flushed up to") === 0 ) {
+ # This number is NOT printed in hex in InnoDB plugin.
+ # Log flushed up to 13093948219327
+ # Log flushed up to 125 3934414864
+ $results['log_bytes_flushed']
+ = isset($row[5])
+ ? make_bigint($row[4], $row[5])
+ : to_int($row[4]);
+ }
+ elseif (strpos($line, "Last checkpoint at") === 0 ) {
+ # Last checkpoint at 125 3934293461
+ $results['last_checkpoint']
+ = isset($row[4])
+ ? make_bigint($row[3], $row[4])
+ : to_int($row[3]);
+ }
+
+ # BUFFER POOL AND MEMORY
+ elseif (strpos($line, "Total memory allocated") === 0 ) {
+ # Total memory allocated 29642194944; in additional pool allocated 0
+ $results['total_mem_alloc'] = to_int($row[3]);
+ $results['additional_pool_alloc'] = to_int($row[8]);
+ }
+ elseif (strpos($line, 'Adaptive hash index ') === 0 ) {
+ # Adaptive hash index 1538240664 (186998824 + 1351241840)
+ $results['adaptive_hash_memory'] = to_int($row[3]);
+ }
+ elseif (strpos($line, 'Page hash ') === 0 ) {
+ # Page hash 11688584
+ $results['page_hash_memory'] = to_int($row[2]);
+ }
+ elseif (strpos($line, 'Dictionary cache ') === 0 ) {
+ # Dictionary cache 145525560 (140250984 + 5274576)
+ $results['dictionary_cache_memory'] = to_int($row[2]);
+ }
+ elseif (strpos($line, 'File system ') === 0 ) {
+ # File system 313848 (82672 + 231176)
+ $results['file_system_memory'] = to_int($row[2]);
+ }
+ elseif (strpos($line, 'Lock system ') === 0 ) {
+ # Lock system 29232616 (29219368 + 13248)
+ $results['lock_system_memory'] = to_int($row[2]);
+ }
+ elseif (strpos($line, 'Recovery system ') === 0 ) {
+ # Recovery system 0 (0 + 0)
+ $results['recovery_system_memory'] = to_int($row[2]);
+ }
+ elseif (strpos($line, 'Threads ') === 0 ) {
+ # Threads 409336 (406936 + 2400)
+ $results['thread_hash_memory'] = to_int($row[1]);
+ }
+ elseif (strpos($line, 'innodb_io_pattern ') === 0 ) {
+ # innodb_io_pattern 0 (0 + 0)
+ $results['innodb_io_pattern_memory'] = to_int($row[1]);
+ }
+ elseif (strpos($line, "Buffer pool size ") === 0 ) {
+ # The " " after size is necessary to avoid matching the wrong line:
+ # Buffer pool size 1769471
+ # Buffer pool size, bytes 28991012864
+ $results['pool_size'] = to_int($row[3]);
+ }
+ elseif (strpos($line, "Free buffers") === 0 ) {
+ # Free buffers 0
+ $results['free_pages'] = to_int($row[2]);
+ }
+ elseif (strpos($line, "Database pages") === 0 ) {
+ # Database pages 1696503
+ $results['database_pages'] = to_int($row[2]);
+ }
+ elseif (strpos($line, "Modified db pages") === 0 ) {
+ # Modified db pages 160602
+ $results['modified_pages'] = to_int($row[3]);
+ }
+ elseif (strpos($line, "Pages read ahead") === 0 ) {
+ # Must do this BEFORE the next test, otherwise it'll get fooled by this
+ # line from the new plugin (see samples/innodb-015.txt):
+ # Pages read ahead 0.00/s, evicted without access 0.06/s
+ # TODO: No-op for now, see issue 134.
+ }
+ elseif (strpos($line, "Pages read") === 0 ) {
+ # Pages read 15240822, created 1770238, written 21705836
+ $results['pages_read'] = to_int($row[2]);
+ $results['pages_created'] = to_int($row[4]);
+ $results['pages_written'] = to_int($row[6]);
+ }
+
+ # ROW OPERATIONS
+ elseif (strpos($line, 'Number of rows inserted') === 0 ) {
+ # Number of rows inserted 50678311, updated 66425915, deleted 20605903, read 454561562
+ $results['rows_inserted'] = to_int($row[4]);
+ $results['rows_updated'] = to_int($row[6]);
+ $results['rows_deleted'] = to_int($row[8]);
+ $results['rows_read'] = to_int($row[10]);
+ }
+ elseif (strpos($line, " queries inside InnoDB, ") > 0 ) {
+ # 0 queries inside InnoDB, 0 queries in queue
+ $results['queries_inside'] = to_int($row[0]);
+ $results['queries_queued'] = to_int($row[4]);
+ }
+ }
+
+ foreach ( array('spin_waits', 'spin_rounds', 'os_waits') as $key ) {
+ $results[$key] = to_int(array_sum($results[$key]));
+ }
+ $results['unflushed_log']
+ = big_sub($results['log_bytes_written'], $results['log_bytes_flushed']);
+ $results['uncheckpointed_bytes']
+ = big_sub($results['log_bytes_written'], $results['last_checkpoint']);
+
+
+# foreach ($results as $key => $value) {
+# echo(strtolower($key).":".strtolower($value)."\n");
+# }
+
+
+ return $results;
+}
+
+
+# ============================================================================
+# Returns a bigint from two ulint or a single hex number. This is tested in
+# t/mysql_stats.php and copied, without tests, to ss_get_by_ssh.php.
+# ============================================================================
+function make_bigint ($hi, $lo = null) {
+ debug(array($hi, $lo));
+ if (is_null($lo) ) {
+ # Assume it is a hex string representation.
+ return base_convert($hi, 16, 10);
+ }
+ else {
+ $hi = $hi ? $hi : '0'; # Handle empty-string or whatnot
+ $lo = $lo ? $lo : '0';
+ return big_add(big_multiply($hi, 4294967296), $lo);
+ }
+}
+
+# ============================================================================
+# Extracts the numbers from a string. You can't reliably do this by casting to
+# an int, because numbers that are bigger than PHP's int (varies by platform)
+# will be truncated. And you can't use sprintf(%u) either, because the maximum
+# value that will return on some platforms is 4022289582. So this just handles
+# them as a string instead. It extracts digits until it finds a non-digit and
+# quits. This is tested in t/mysql_stats.php and copied, without tests, to
+# ss_get_by_ssh.php.
+# ============================================================================
+function to_int ( $str ) {
+ debug($str);
+ global $debug;
+ preg_match('{(\d+)}', $str, $m);
+ if (isset($m[1]) ) {
+ return $m[1];
+ }
+ elseif ($debug ) {
+ print_r(debug_backtrace());
+ }
+ else {
+ return 0;
+ }
+}
+
+# ============================================================================
+# Wrap mysql_query in error-handling, and instead of returning the result,
+# return an array of arrays in the result.
+# ============================================================================
+function run_query($sql, $conn) {
+ global $debug;
+ debug($sql);
+ $result = @mysql_query($sql, $conn);
+ if ($debug ) {
+ $error = @mysql_error($conn);
+ if ($error ) {
+ debug(array($sql, $error));
+ die("SQLERR $error in $sql");
+ }
+ }
+ $array = array();
+ while ( $row = @mysql_fetch_array($result) ) {
+ $array[] = $row;
+ }
+ debug(array($sql, $array));
+ return $array;
+}
+
+# ============================================================================
+# Safely increments a value that might be null.
+# ============================================================================
+function increment(&$arr, $key, $howmuch) {
+ debug(array($key, $howmuch));
+ if (array_key_exists($key, $arr) && isset($arr[$key]) ) {
+ $arr[$key] = big_add($arr[$key], $howmuch);
+ }
+ else {
+ $arr[$key] = $howmuch;
+ }
+}
+
+# ============================================================================
+# Multiply two big integers together as accurately as possible with reasonable
+# effort. This is tested in t/mysql_stats.php and copied, without tests, to
+# ss_get_by_ssh.php. $force is for testability.
+# ============================================================================
+function big_multiply ($left, $right, $force = null) {
+ if (function_exists("gmp_mul") && (is_null($force) || $force == 'gmp') ) {
+ debug(array('gmp_mul', $left, $right));
+ return gmp_strval( gmp_mul( $left, $right ));
+ }
+ elseif (function_exists("bcmul") && (is_null($force) || $force == 'bc') ) {
+ debug(array('bcmul', $left, $right));
+ return bcmul( $left, $right );
+ }
+ else { # Or $force == 'something else'
+ debug(array('sprintf', $left, $right));
+ return sprintf("%.0f", $left * $right);
+ }
+}
+
+# ============================================================================
+# Subtract two big integers as accurately as possible with reasonable effort.
+# This is tested in t/mysql_stats.php and copied, without tests, to
+# ss_get_by_ssh.php. $force is for testability.
+# ============================================================================
+function big_sub ($left, $right, $force = null) {
+ debug(array($left, $right));
+ if (is_null($left) ) { $left = 0; }
+ if (is_null($right) ) { $right = 0; }
+ if (function_exists("gmp_sub") && (is_null($force) || $force == 'gmp')) {
+ debug(array('gmp_sub', $left, $right));
+ return gmp_strval( gmp_sub( $left, $right ));
+ }
+ elseif (function_exists("bcsub") && (is_null($force) || $force == 'bc')) {
+ debug(array('bcsub', $left, $right));
+ return bcsub( $left, $right );
+ }
+ else { # Or $force == 'something else'
+ debug(array('to_int', $left, $right));
+ return to_int($left - $right);
+ }
+}
+
+# ============================================================================
+# Add two big integers together as accurately as possible with reasonable
+# effort. This is tested in t/mysql_stats.php and copied, without tests, to
+# ss_get_by_ssh.php. $force is for testability.
+# ============================================================================
+function big_add ($left, $right, $force = null) {
+ if (is_null($left) ) { $left = 0; }
+ if (is_null($right) ) { $right = 0; }
+ if (function_exists("gmp_add") && (is_null($force) || $force == 'gmp')) {
+ debug(array('gmp_add', $left, $right));
+ return gmp_strval( gmp_add( $left, $right ));
+ }
+ elseif (function_exists("bcadd") && (is_null($force) || $force == 'bc')) {
+ debug(array('bcadd', $left, $right));
+ return bcadd( $left, $right );
+ }
+ else { # Or $force == 'something else'
+ debug(array('to_int', $left, $right));
+ return to_int($left + $right);
+ }
+}
+
+# ============================================================================
+# Writes to a debugging log.
+# ============================================================================
+function debug($val) {
+ global $debug_log;
+ if (!$debug_log ) {
+ return;
+ }
+ if ($fp = fopen($debug_log, 'a+') ) {
+ $trace = debug_backtrace();
+ $calls = array();
+ $i = 0;
+ $line = 0;
+ $file = '';
+ foreach ( debug_backtrace() as $arr ) {
+ if ($i++ ) {
+ $calls[] = "$arr[function]() at $file:$line";
+ }
+ $line = array_key_exists('line', $arr) ? $arr['line'] : '?';
+ $file = array_key_exists('file', $arr) ? $arr['file'] : '?';
+ }
+ if (!count($calls) ) {
+ $calls[] = "at $file:$line";
+ }
+ fwrite($fp, date('Y-m-d h:i:s') . ' ' . implode(' <- ', $calls));
+ fwrite($fp, "\n" . var_export($val, TRUE) . "\n");
+ fclose($fp);
+ }
+ else { # Disable logging
+ print("Warning: disabling debug logging to $debug_log\n");
+ $debug_log = FALSE;
+ }
+}
+
+?>
diff --git a/scripts/agent-local/nginx b/scripts/agent-local/nginx
new file mode 100755
index 000000000..11a19cf3c
--- /dev/null
+++ b/scripts/agent-local/nginx
@@ -0,0 +1,37 @@
+#!/usr/bin/env python
+import urllib2
+import re
+
+
+data = urllib2.urlopen('http://localhost/nginx-status').read()
+
+params = {}
+
+for line in data.split("\n"):
+ smallstat = re.match(r"\s?Reading:\s(.*)\sWriting:\s(.*)\sWaiting:\s(.*)$", line)
+ req = re.match(r"\s+(\d+)\s+(\d+)\s+(\d+)", line)
+ if smallstat:
+ params["Reading"] = smallstat.group(1)
+ params["Writing"] = smallstat.group(2)
+ params["Waiting"] = smallstat.group(3)
+ elif req:
+ params["Requests"] = req.group(3)
+ else:
+ pass
+
+
+dataorder = [
+ "Active",
+ "Reading",
+ "Writing",
+ "Waiting",
+ "Requests"
+ ]
+
+
+for param in dataorder:
+ if param == "Active":
+ Active = int(params["Reading"]) + int(params["Writing"]) + int(params["Waiting"])
+ print Active
+ else:
+ print params[param]