Check_oracle_health之增加監控會話數
Check_oracle_health之增加監控會話數
指令碼修改部分已用紅色標註:
#! /usr/bin/perl -w
$ENV{"ORACLE_HOME"}="/app/oracle/ora11g";
#$ENV{"NLS_LANG"}="AMERICAN_AMERICA.ZHS16GBK";
my %ERRORS=( OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 );
my %ERRORCODES=( 0 => 'OK', 1 => 'WARNING', 2 => 'CRITICAL', 3 => 'UNKNOWN' );
package DBD::Oracle::Server::Instance::SGA::DataBuffer;
use strict;
our @ISA = qw(DBD::Oracle::Server::Instance::SGA);
sub new {
my $class = shift;
my %params = @_;
my $self = {
handle => $params{handle},
sum_physical_reads => undef,
sum_physical_reads_direct => undef,
sum_physical_reads_direct_lob => undef,
sum_session_logical_reads => undef,
hitratio => undef,
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
};
bless $self, $class;
$self->init(%params);
return $self;
}
sub init {
my $self = shift;
my %params = @_;
$self->init_nagios();
if ($params{mode} =~ /server::instance::sga::databuffer::hitratio/) {
($self->{sum_physical_reads}, $self->{sum_physical_reads_direct},
$self->{sum_physical_reads_direct_lob},
$self->{sum_session_logical_reads}) =
$self->{handle}->fetchrow_array(q{
SELECT SUM(DECODE(name, 'physical reads', value, 0)),
SUM(DECODE(name, 'physical reads direct', value, 0)),
SUM(DECODE(name, 'physical reads direct (lob)', value, 0)),
SUM(DECODE(name, 'session logical reads', value, 0))
FROM sys.v_$sysstat
});
if (! defined $self->{sum_physical_reads}) {
$self->add_nagios_critical("unable to get sga buffer cache");
} else {
$self->valdiff(\%params, qw(sum_physical_reads sum_physical_reads_direct
sum_physical_reads_direct_lob sum_session_logical_reads));
$self->{hitratio} = $self->{delta_sum_session_logical_reads} ?
100 - 100 * ((
$self->{delta_sum_physical_reads} -
$self->{delta_sum_physical_reads_direct_lob} -
$self->{delta_sum_physical_reads_direct}) /
$self->{delta_sum_session_logical_reads}) : 0;
}
}
}
sub nagios {
my $self = shift;
my %params = @_;
if (! $self->{nagios_level}) {
if ($params{mode} =~ /server::instance::sga::databuffer::hitratio/) {
$self->add_nagios(
$self->check_thresholds($self->{hitratio}, "98:", "95:"),
sprintf "SGA data buffer hit ratio %.2f%%", $self->{hitratio});
$self->add_perfdata(sprintf "sga_data_buffer_hit_ratio=%.2f%%;%s;%s",
$self->{hitratio},
$self->{warningrange}, $self->{criticalrange});
}
}
}
package DBD::Oracle::Server::Instance::SGA::SharedPool::DictionaryCache;
use strict;
our @ISA = qw(DBD::Oracle::Server::Instance::SGA::SharedPool);
sub new {
my $class = shift;
my %params = @_;
my $self = {
handle => $params{handle},
sum_gethits => undef,
sum_gets => undef,
hitratio => undef,
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
};
bless $self, $class;
$self->init(%params);
return $self;
}
sub init {
my $self = shift;
my %params = @_;
$self->init_nagios();
if ($params{mode} =~
/server::instance::sga::sharedpool::dictionarycache::hitratio/) {
($self->{sum_gets}, $self->{sum_gethits}) =
$self->{handle}->fetchrow_array(q{
SELECT SUM(gets), SUM(gets-getmisses) FROM v$rowcache
});
if (! defined $self->{sum_gets}) {
$self->add_nagios_critical("unable to get sga dc");
} else {
$self->valdiff(\%params, qw(sum_gets sum_gethits));
$self->{hitratio} = $self->{delta_sum_gets} ?
(100 * $self->{delta_sum_gethits} / $self->{delta_sum_gets}) : 0;
}
}
}
sub nagios {
my $self = shift;
my %params = @_;
if (! $self->{nagios_level}) {
if ($params{mode} =~
/server::instance::sga::sharedpool::dictionarycache::hitratio/) {
$self->add_nagios(
$self->check_thresholds($self->{hitratio}, "95:", "90:"),
sprintf "SGA dictionary cache hit ratio %.2f%%", $self->{hitratio});
$self->add_perfdata(sprintf "sga_dictionary_cache_hit_ratio=%.2f%%;%s;%s",
$self->{hitratio}, $self->{warningrange}, $self->{criticalrange});
}
}
}
package DBD::Oracle::Server::Instance::SGA::SharedPool::LibraryCache;
use strict;
our @ISA = qw(DBD::Oracle::Server::Instance::SGA::SharedPool);
sub new {
my $class = shift;
my %params = @_;
my $self = {
handle => $params{handle},
sum_gets => undef,
sum_gethits => undef,
sum_pins => undef,
sum_pinhits => undef,
get_hitratio => undef,
pin_hitratio => undef,
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
};
bless $self, $class;
$self->init(%params);
return $self;
}
sub init {
my $self = shift;
my %params = @_;
$self->init_nagios();
if ($params{mode} =~
/server::instance::sga::sharedpool::librarycache::hitratio/) {
($self->{sum_gethits}, $self->{sum_gets}, $self->{sum_pinhits},
$self->{sum_pins}) = $self->{handle}->fetchrow_array(q{
SELECT SUM(gethits), SUM(gets), SUM(pinhits), SUM(pins)
FROM v$librarycache
});
if (! defined $self->{sum_gets} || ! defined $self->{sum_pinhits}) {
$self->add_nagios_critical("unable to get sga lc");
} else {
$self->valdiff(\%params, qw(sum_gets sum_gethits sum_pins sum_pinhits));
$self->{get_hitratio} = $self->{delta_sum_gets} ?
(100 * $self->{delta_sum_gethits} / $self->{delta_sum_gets}) : 0;
$self->{pin_hitratio} = $self->{delta_sum_pins} ?
(100 * $self->{delta_sum_pinhits} / $self->{delta_sum_pins}) : 0;
}
}
}
sub nagios {
my $self = shift;
my %params = @_;
if (! $self->{nagios_level}) {
if ($params{mode} =~
/server::instance::sga::sharedpool::librarycache::hitratio/) {
$self->add_nagios(
$self->check_thresholds($self->{get_hitratio}, "98:", "95:"),
sprintf "SGA library cache hit ratio %.2f%%", $self->{get_hitratio});
$self->add_perfdata(sprintf "sga_library_cache_hit_ratio=%.2f%%;%s;%s",
$self->{get_hitratio}, $self->{warningrange}, $self->{criticalrange});
}
}
}
package DBD::Oracle::Server::Instance::SGA::SharedPool;
use strict;
our @ISA = qw(DBD::Oracle::Server::Instance::SGA);
sub new {
my $class = shift;
my %params = @_;
my $self = {
free => undef,
reloads => undef,
pins => undef,
handle => $params{handle},
library_cache => undef,
dictionary_cache => undef,
parse_soft => undef,
parse_hard => undef,
parse_failures => undef,
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
};
bless $self, $class;
$self->init(%params);
return $self;
}
sub init {
my $self = shift;
my %params = @_;
$self->init_nagios();
if ($params{mode} =~ /server::instance::sga::sharedpool::librarycache/) {
$self->{library_cache} =
DBD::Oracle::Server::Instance::SGA::SharedPool::LibraryCache->new(
%params);
} elsif ($params{mode} =~ /server::instance::sga::sharedpool::dictionarycache/) {
$self->{dictionary_cache} =
DBD::Oracle::Server::Instance::SGA::SharedPool::DictionaryCache->new(
%params);
} elsif ($params{mode} eq "server::instance::sga::sharedpool::free") {
$self->init_shared_pool_free(%params);
} elsif ($params{mode} eq "server::instance::sga::sharedpool::reloads") {
$self->init_shared_pool_reloads(%params);
} elsif ($params{mode} eq "server::instance::sga::sharedpool::softparse") {
$self->init_shared_pool_parser(%params);
}
}
sub init_shared_pool_reloads {
my $self = shift;
my %params = @_;
($self->{reloads}, $self->{pins}) = $self->{handle}->fetchrow_array(q{
SELECT SUM(reloads), SUM(pins)
FROM v$librarycache
WHERE namespace IN ('SQL AREA','TABLE/PROCEDURE','BODY','TRIGGER')
});
if (! defined $self->{reloads}) {
$self->add_nagios_critical("unable to get sga reloads");
} else {
$self->valdiff(\%params, qw(reloads pins));
$self->{reload_ratio} = $self->{delta_pins} ?
100 * $self->{delta_reloads} / $self->{delta_pins} : 100;
}
}
sub init_shared_pool_free {
my $self = shift;
my %params = @_;
$self->{free_percent} = $self->{handle}->fetchrow_array(q{
SELECT ROUND((SUM(DECODE(name, 'free memory', bytes, 0)) /
SUM(bytes)) * 100,2) FROM v$sgastat where pool = 'shared pool'
});
# scheint nur bis ora9 sinnvoll zu sein. >10.x liefert 0
#$self->{alloc} = $self->{handle}->fetchrow_array(q{
# SELECT value FROM v$parameter WHERE name = 'shared_pool_size'
#});
if (! defined $self->{free_percent}) {
$self->add_nagios_critical("unable to get sga free");
return undef;
}
}
sub init_shared_pool_parser {
my $self = shift;
my %params = @_;
($self->{parse_total}, $self->{parse_hard}, $self->{parse_failures}) =
$self->{handle}->fetchrow_array(q{
SELECT
(SELECT value FROM v$sysstat WHERE name = 'parse count (total)'),
(SELECT value FROM v$sysstat WHERE name = 'parse count (hard)'),
(SELECT value FROM v$sysstat WHERE name = 'parse count (failures)')
FROM DUAL
});
if (! defined $self->{parse_total}) {
$self->add_nagios_critical("unable to get parser");
} else {
$self->valdiff(\%params, qw(parse_total parse_hard parse_failures));
$self->{parse_soft_ratio} = $self->{delta_parse_total} ?
100 * ($self->{delta_parse_total} - $self->{delta_parse_hard}) /
$self->{delta_parse_total} : 100;
}
}
sub nagios {
my $self = shift;
my %params = @_;
if (! $self->{nagios_level}) {
if ($params{mode} =~ /server::instance::sga::sharedpool::librarycache/) {
$self->{library_cache}->nagios(%params);
$self->merge_nagios($self->{library_cache});
} elsif ($params{mode} =~ /server::instance::sga::sharedpool::dictionarycache/) {
$self->{dictionary_cache}->nagios(%params);
$self->merge_nagios($self->{dictionary_cache});
} elsif ($params{mode} eq "server::instance::sga::sharedpool::free") {
$self->add_nagios(
$self->check_thresholds($self->{free_percent}, "10:", "5:"),
sprintf "SGA shared pool free %.2f%%", $self->{free_percent});
$self->add_perfdata(sprintf "sga_shared_pool_free=%.2f%%;%s;%s",
$self->{free_percent}, $self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} eq "server::instance::sga::sharedpool::reloads") {
$self->add_nagios(
$self->check_thresholds($self->{reload_ratio}, "1", "10"),
sprintf "SGA shared pool reload ratio %.2f%%", $self->{reload_ratio});
$self->add_perfdata(sprintf "sga_shared_pool_reload_ratio=%.2f%%;%s;%s",
$self->{reload_ratio}, $self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} eq "server::instance::sga::sharedpool::softparse") {
$self->add_nagios(
$self->check_thresholds( $self->{parse_soft_ratio}, "98:", "90:"),
sprintf "Soft parse ratio %.2f%%", $self->{parse_soft_ratio});
$self->add_perfdata(sprintf "soft_parse_ratio=%.2f%%;%s;%s",
$self->{parse_soft_ratio},
$self->{warningrange}, $self->{criticalrange});
}
}
}
package DBD::Oracle::Server::Instance::SGA::RollbackSegments;
use strict;
our @ISA = qw(DBD::Oracle::Server::Instance::SGA);
# only create one object with new which stands for all rollback segments
sub new {
my $class = shift;
my %params = @_;
my $self = {
handle => $params{handle},
gets => undef,
waits => undef,
wraps => undef,
extends => undef,
undo_header_waits => undef,
undo_block_waits => undef,
rollback_segment_hit_ratio => undef,
rollback_segment_header_contention => undef,
rollback_segment_block_contention => undef,
rollback_segment_extents => undef,
rollback_segment_wraps => undef,
rollback_segment_wraps_persec => undef,
rollback_segment_extends => undef,
rollback_segment_extends_persec => undef,
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
};
bless $self, $class;
$self->init(%params);
return $self;
}
sub init {
my $self = shift;
my %params = @_;
$self->init_nagios();
if ($params{mode} =~ /server::instance::sga::rollbacksegments::wraps/) {
$self->{wraps} = $self->{handle}->fetchrow_array(q{
SELECT SUM(wraps) FROM v$rollstat
});
if (! defined $self->{wraps}) {
$self->add_nagios_critical("unable to get rollback segments stats");
} else {
$self->valdiff(\%params, qw(wraps));
$self->{rollback_segment_wraps} = $self->{delta_wraps};
$self->{rollback_segment_wraps_persec} = $self->{delta_wraps} /
$self->{delta_timestamp};
}
} elsif ($params{mode} =~
/server::instance::sga::rollbacksegments::extends/) {
$self->{extends} = $self->{handle}->fetchrow_array(q{
SELECT SUM(extends) FROM v$rollstat
});
if (! defined $self->{extends}) {
$self->add_nagios_critical("unable to get rollback segments stats");
} else {
$self->valdiff(\%params, qw(extends));
$self->{rollback_segment_extends} = $self->{delta_extends};
$self->{rollback_segment_extends_persec} = $self->{delta_extends} /
$self->{delta_timestamp};
}
} elsif ($params{mode} =~
/server::instance::sga::rollbacksegments::headercontention/) {
($self->{undo_header_waits}, $self->{waits}) = $self->{handle}->fetchrow_array(q{
SELECT (
SELECT SUM(count)
FROM v$waitstat
WHERE class = 'undo header' OR class = 'system undo header'
) undo, (
SELECT SUM(count)
FROM v$waitstat
) complete
FROM DUAL
});
if (! defined $self->{undo_header_waits}) {
$self->add_nagios_critical("unable to get rollback segments wait stats");
} else {
$self->valdiff(\%params, qw(undo_header_waits waits));
$self->{rollback_segment_header_contention} =
$self->{delta_waits} ? 100 * $self->{delta_undo_header_waits} / $self->{delta_waits} : 0;
}
} elsif ($params{mode} =~
/server::instance::sga::rollbacksegments::blockcontention/) {
($self->{undo_block_waits}, $self->{waits}) = $self->{handle}->fetchrow_array(q{
SELECT (
SELECT SUM(count)
FROM v$waitstat
WHERE class = 'undo block' OR class = 'system undo block'
) undo, (
SELECT SUM(count)
FROM v$waitstat
) complete
FROM DUAL
});
if (! defined $self->{undo_block_waits}) {
$self->add_nagios_critical("unable to get rollback segments wait stats");
} else {
$self->valdiff(\%params, qw(undo_block_waits waits));
$self->{rollback_segment_block_contention} =
$self->{delta_waits} ? 100 * $self->{delta_undo_block_waits} / $self->{delta_waits} : 0;
}
} elsif ($params{mode} =~
/server::instance::sga::rollbacksegments::hitratio/) {
($self->{waits}, $self->{gets}) = $self->{handle}->fetchrow_array(q{
SELECT SUM(waits), SUM(gets) FROM v$rollstat
});
if (! defined $self->{gets}) {
$self->add_nagios_critical("unable to get rollback segments wait stats");
} else {
$self->valdiff(\%params, qw(waits gets));
$self->{rollback_segment_hit_ratio} = $self->{delta_gets} ?
100 - 100 * $self->{delta_waits} / $self->{delta_gets} : 100;
}
} elsif ($params{mode} =~
/server::instance::sga::rollbacksegments::avgactivesize/) {
if ($params{selectname}) {
$self->{rollback_segment_optimization_size} = $self->{handle}->fetchrow_array(q{
SELECT AVG(s.optsize / 1048576) optmization_size
FROM v$rollstat s, v$rollname n
WHERE s.usn = n.usn AND n.name != 'SYSTEM' AND n.name = ?
}, $params{selectname}) || 0;
$self->{rollback_segment_average_active} = $self->{handle}->fetchrow_array(q{
SELECT AVG(s.aveactive / 1048576) average_active
FROM v$rollstat s, v$rollname n
WHERE s.usn = n.usn AND n.name != 'SYSTEM' AND n.name = ?
}, $params{selectname}) || 0;
} else {
$self->{rollback_segment_optimization_size} = $self->{handle}->fetchrow_array(q{
SELECT AVG(s.optsize / 1048576) optmization_size
FROM v$rollstat s, v$rollname n
WHERE s.usn = n.usn AND n.name != 'SYSTEM'
}) || 0;
$self->{rollback_segment_average_active} = $self->{handle}->fetchrow_array(q{
SELECT AVG(s.aveactive / 1048576) average_active
FROM v$rollstat s, v$rollname n
WHERE s.usn = n.usn AND n.name != 'SYSTEM'
}) || 0;
}
} else {
}
}
sub nagios {
my $self = shift;
my %params = @_;
if (! $self->{nagios_level}) {
if ($params{mode} =~ /server::instance::sga::rollbacksegments::wraps/) {
if ($params{absolute}) {
$self->add_nagios(
$self->check_thresholds(
$self->{rollback_segment_wraps}, "1", "100"),
sprintf "Rollback segment wraps %d times",
$self->{rollback_segment_wraps});
} else {
$self->add_nagios(
$self->check_thresholds(
$self->{rollback_segment_wraps_persec}, "1", "100"),
sprintf "Rollback segment wraps %.2f/sec",
$self->{rollback_segment_wraps_persec});
}
$self->add_perfdata(
sprintf "rollback_segment_wraps=%d;%s;%s",
$self->{rollback_segment_wraps},
$self->{warningrange}, $self->{criticalrange});
$self->add_perfdata(
sprintf "rollback_segment_wraps_rate=%.2f;%s;%s",
$self->{rollback_segment_wraps_persec},
$self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~
/server::instance::sga::rollbacksegments::extends/) {
if ($params{absolute}) {
$self->add_nagios(
$self->check_thresholds(
$self->{rollback_segment_extends}, "1", "100"),
sprintf "Rollback segment extends %d times",
$self->{rollback_segment_extends});
} else {
$self->add_nagios(
$self->check_thresholds(
$self->{rollback_segment_extends_persec}, "1", "100"),
sprintf "Rollback segment extends %.2f/sec",
$self->{rollback_segment_extends_persec});
}
$self->add_perfdata(
sprintf "rollback_segment_extends=%d;%s;%s",
$self->{rollback_segment_extends},
$self->{warningrange}, $self->{criticalrange});
$self->add_perfdata(
sprintf "rollback_segment_extends_rate=%.2f;%s;%s",
$self->{rollback_segment_extends_persec},
$self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~
/server::instance::sga::rollbacksegments::headercontention/) {
$self->add_nagios(
$self->check_thresholds(
$self->{rollback_segment_header_contention}, "1", "2"),
sprintf "Rollback segment header contention is %.2f%%",
$self->{rollback_segment_header_contention});
$self->add_perfdata(
sprintf "rollback_segment_header_contention=%.2f%%;%s;%s",
$self->{rollback_segment_header_contention},
$self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~
/server::instance::sga::rollbacksegments::blockcontention/) {
$self->add_nagios(
$self->check_thresholds(
$self->{rollback_segment_block_contention}, "1", "2"),
sprintf "Rollback segment block contention is %.2f%%",
$self->{rollback_segment_block_contention});
$self->add_perfdata(
sprintf "rollback_segment_block_contention=%.2f%%;%s;%s",
$self->{rollback_segment_block_contention},
$self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~
/server::instance::sga::rollbacksegments::hitratio/) {
$self->add_nagios(
$self->check_thresholds(
$self->{rollback_segment_hit_ratio}, "99:", "98:"),
sprintf "Rollback segment hit ratio is %.2f%%",
$self->{rollback_segment_hit_ratio});
$self->add_perfdata(
sprintf "rollback_segment_hit_ratio=%.2f%%;%s;%s",
$self->{rollback_segment_hit_ratio},
$self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~
/server::instance::sga::rollbacksegments::avgactivesize/) {
$self->add_nagios_ok(sprintf "Rollback segment average size %.2f MB",
$self->{rollback_segment_average_active});
$self->add_perfdata(
sprintf "rollback_segment_avgsize=%.2f rollback_segment_optsize=%.2f",
$self->{rollback_segment_average_active},
$self->{rollback_segment_optimization_size});
}
}
}
package DBD::Oracle::Server::Instance::SGA::RedoLogBuffer;
use strict;
our @ISA = qw(DBD::Oracle::Server::Instance::SGA);
sub new {
my $class = shift;
my %params = @_;
my $self = {
handle => $params{handle},
last_switch_interval => undef,
redo_buffer_allocation_retries => undef,
redo_entries => undef,
retry_ratio => undef,
redo_size => undef,
redo_size_per_sec => undef,
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
};
bless $self, $class;
$self->init(%params);
return $self;
}
sub init {
my $self = shift;
my %params = @_;
$self->init_nagios();
if ($params{mode} =~ /server::instance::sga::redologbuffer::switchinterval/) {
if ($self->instance_rac()) {
eval {
# alles was jemals geswitcht hat, letzter switch, zweitletzter switch
# jetzt - letzter switch = mindestlaenge des naechsten intervals
# wenn das lang genug ist, dann war das letzte, kurze intervall
# wohl nur ein ausreisser oder manueller switch
# derzeit laufendes intervall, letztes intervall, vorletztes intervall
($self->{next_switch_interval}, $self->{last_switch_interval}, $self->{nextto_last_switch_interval}) =
$self->{handle}->fetchrow_array(q {
WITH temptab AS
(
SELECT sequence#, first_time FROM sys.v_$log WHERE status = 'CURRENT'
AND thread# = ?
UNION ALL
SELECT sequence#, first_time FROM sys.v_$log_history
WHERE thread# = ?
ORDER BY first_time DESC
)
SELECT
(sysdate - a.first_time) * 1440 * 60 thisinterval,
(a.first_time - b.first_time) * 1440 * 60 lastinterval,
(b.first_time - c.first_time) * 1440 * 60 nexttolastinterval
FROM
(
SELECT NVL(
(
SELECT first_time FROM (
SELECT first_time, rownum AS irow FROM temptab WHERE ROWNUM <= 1
) WHERE irow = 1
) , to_date('20090624','YYYYMMDD')) as first_time FROM dual
) a,
(
SELECT NVL(
(
SELECT first_time FROM (
SELECT first_time, rownum AS irow FROM temptab WHERE ROWNUM <= 2
) WHERE irow = 2
) , to_date('20090624','YYYYMMDD')) as first_time FROM dual
) b,
(
SELECT NVL(
(
SELECT first_time FROM (
SELECT first_time, rownum AS irow FROM temptab WHERE ROWNUM <= 3
) WHERE irow = 3
) , to_date('20090624','YYYYMMDD')) as first_time FROM dual
) c
}, $self->instance_thread(), $self->instance_thread());
};
} else {
eval {
# alles was jemals geswitcht hat, letzter switch, zweitletzter switch
# jetzt - letzter switch = mindestlaenge des naechsten intervals
# wenn das lang genug ist, dann war das letzte, kurze intervall
# wohl nur ein ausreisser oder manueller switch
# derzeit laufendes intervall, letztes intervall, vorletztes intervall
($self->{next_switch_interval}, $self->{last_switch_interval}, $self->{nextto_last_switch_interval}) =
$self->{handle}->fetchrow_array(q {
WITH temptab AS
(
SELECT sequence#, first_time FROM sys.v_$log WHERE status = 'CURRENT'
UNION ALL
SELECT sequence#, first_time FROM sys.v_$log_history ORDER BY first_time DESC
)
SELECT
(sysdate - a.first_time) * 1440 * 60 thisinterval,
(a.first_time - b.first_time) * 1440 * 60 lastinterval,
(b.first_time - c.first_time) * 1440 * 60 nexttolastinterval
FROM
(
SELECT NVL(
(
SELECT first_time FROM (
SELECT first_time, rownum AS irow FROM temptab WHERE ROWNUM <= 1
) WHERE irow = 1
) , to_date('20090624','YYYYMMDD')) as first_time FROM dual
) a,
(
SELECT NVL(
(
SELECT first_time FROM (
SELECT first_time, rownum AS irow FROM temptab WHERE ROWNUM <= 2
) WHERE irow = 2
) , to_date('20090624','YYYYMMDD')) as first_time FROM dual
) b,
(
SELECT NVL(
(
SELECT first_time FROM (
SELECT first_time, rownum AS irow FROM temptab WHERE ROWNUM <= 3
) WHERE irow = 3
) , to_date('20090624','YYYYMMDD')) as first_time FROM dual
) c
});
};
}
if (! defined $self->{last_switch_interval}) {
$self->add_nagios_critical(
sprintf "unable to get last switch interval");
}
} elsif ($params{mode} =~ /server::instance::sga::redologbuffer::retryratio/) {
($self->{redo_buffer_allocation_retries}, $self->{redo_entries}) =
$self->{handle}->fetchrow_array(q{
SELECT a.value, b.value
FROM v$sysstat a, v$sysstat b
WHERE a.name = 'redo buffer allocation retries'
AND b.name = 'redo entries'
});
if (! defined $self->{redo_buffer_allocation_retries}) {
$self->add_nagios_critical("unable to get retry ratio");
} else {
$self->valdiff(\%params, qw(redo_buffer_allocation_retries redo_entries));
$self->{retry_ratio} = $self->{delta_redo_entries} ?
100 * $self->{delta_redo_buffer_allocation_retries} / $self->{delta_redo_entries} : 0;
}
} elsif ($params{mode} =~ /server::instance::sga::redologbuffer::iotraffic/) {
$self->{redo_size} = $self->{handle}->fetchrow_array(q{
SELECT value FROM v$sysstat WHERE name = 'redo size'
});
if (! defined $self->{redo_size}) {
$self->add_nagios_critical("unable to get redo size");
} else {
$self->valdiff(\%params, qw(redo_size));
$self->{redo_size_per_sec} =
$self->{delta_redo_size} / $self->{delta_timestamp};
# Megabytes / sec
$self->{redo_size_per_sec} = $self->{redo_size_per_sec} / 1048576;
}
}
}
sub nagios {
my $self = shift;
my %params = @_;
if (! $self->{nagios_level}) {
if ($params{mode} =~
/server::instance::sga::redologbuffer::switchinterval/) {
my $nextlevel = $self->check_thresholds($self->{next_switch_interval}, "600:", "60:");
my $nexttolastlevel = $self->check_thresholds($self->{nextto_last_switch_interval}, "600:", "60:");
my $lastlevel = $self->check_thresholds($self->{last_switch_interval}, "600:", "60:");
if ($lastlevel) {
# nachschauen, ob sich die situation schon entspannt hat
if ($nextlevel == 2) {
# das riecht nach aerger. kann zwar auch daran liegen, weil der check unmittelbar nach deim kurzen switch
# ausgefuehrt wird, aber dann bleibts beim soft-hard und beim retry schauts schon besser aus.
$self->add_nagios(
# 10: minutes, 1: minute = 600:, 60:
$nextlevel,
sprintf "Last redo log file switch interval was %d minutes%s. Next interval presumably >%d minutes",
$self->{last_switch_interval} / 60,
$self->instance_rac() ? sprintf " (thread %d)", $self->instance_thread() : "",
$self->{next_switch_interval} / 60);
} elsif ($nextlevel == 1) {
# das kommt daher, weil retry_interval < warningthreshold
if ($nexttolastlevel) {
# aber vorher war auch schon was faul. da braut sich vieleicht was zusammen.
# die warnung ist sicher berechtigt.
$self->add_nagios(
$nextlevel,
sprintf "Last redo log file switch interval was %d minutes%s. Next interval presumably >%d minutes. Second incident in a row.",
$self->{last_switch_interval} / 60,
$self->instance_rac() ? sprintf " (thread %d)", $self->instance_thread() : "",
$self->{next_switch_interval} / 60);
} else {
# hier bin ich grosszuegig. vorletztes intervall war ok, letztes intervall war nicht ok.
# ich rechne mir also chancen aus, dass $nextlevel nur auf warning ist, weil der retry zu schnell
# nach dem letzten switch stattfindet. sollte sich entspannen und wenns wirklich ein problem gibt
# dann kommt sowieso wieder ein switch. also erstmal ok.
$self->add_nagios(
0,
sprintf "Last redo log file switch interval was %d minutes%s. Next interval presumably >%d minutes. Probably a single incident.",
$self->{last_switch_interval} / 60,
$self->instance_rac() ? sprintf " (thread %d)", $self->instance_thread() : "",
$self->{next_switch_interval} / 60);
}
} else {
# war wohl ein einzelfall. also gehen wir davon aus, dass das warninglevel nur wegen des retrys
# unterschritten wurde und der naechste switch wieder lange genug sein wird
$self->add_nagios(
$nextlevel, # sollte 0 sein
sprintf "Last redo log file switch interval was %d minutes%s. Next interval presumably >%d minutes",
$self->{last_switch_interval} / 60,
$self->instance_rac() ? sprintf " (thread %d)", $self->instance_thread() : "",
$self->{next_switch_interval} / 60);
}
} else {
$self->add_nagios(
$lastlevel,
sprintf "Last redo log file switch interval was %d minutes%s. Next interval presumably >%d minutes",
$self->{last_switch_interval} / 60,
$self->instance_rac() ? sprintf " (thread %d)", $self->instance_thread() : "",
$self->{next_switch_interval} / 60);
}
$self->add_perfdata(sprintf "redo_log_file_switch_interval=%ds;%s;%s",
$self->{last_switch_interval},
$self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~
/server::instance::sga::redologbuffer::retryratio/) {
$self->add_nagios(
$self->check_thresholds($self->{retry_ratio}, "1", "10"),
sprintf "Redo log retry ratio is %.6f%%",$self->{retry_ratio});
$self->add_perfdata(sprintf "redo_log_retry_ratio=%.6f%%;%s;%s",
$self->{retry_ratio},
$self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~
/server::instance::sga::redologbuffer::iotraffic/) {
$self->add_nagios(
$self->check_thresholds($self->{redo_size_per_sec}, "100", "200"),
sprintf "Redo log io is %.6f MB/sec", $self->{redo_size_per_sec});
$self->add_perfdata(sprintf "redo_log_io_per_sec=%.6f;%s;%s",
$self->{redo_size_per_sec},
$self->{warningrange}, $self->{criticalrange});
}
}
}
package DBD::Oracle::Server::Instance::SGA::Latch;
our @ISA = qw(DBD::Oracle::Server::Instance::SGA);
{
my @latches = ();
my $initerrors = undef;
sub add_latch {
push(@latches, shift);
}
sub return_latches {
my %params = @_;
if ($params{mode} =~ /server::instance::sga::latch::contention/) {
return reverse
sort { $a->{contention} <=> $b->{contention} } @latches;
} else {
return reverse
sort { $a->{name} cmp $b->{name} } @latches;
}
}
sub init_latches {
my %params = @_;
my $num_latches = 0;
if (($params{mode} =~ /server::instance::sga::latch::contention/) ||
($params{mode} =~ /server::instance::sga::latch::waiting/) ||
($params{mode} =~ /server::instance::sga::latch::hitratio/) ||
($params{mode} =~ /server::instance::sga::latch::listlatches/)) {
my $sumsleeps = $params{handle}->fetchrow_array(q{
SELECT SUM(sleeps) FROM v$latch
});
my @latchresult = $params{handle}->fetchall_array(q{
SELECT latch#, name, gets, sleeps, misses, wait_time
FROM v$latch
});
foreach (@latchresult) {
my ($number, $name, $gets, $sleeps, $misses, $wait_time) = @{$_};
if ($params{regexp}) {
next if $params{selectname} && $name !~ /$params{selectname}/;
} else {
next if ($params{selectname} && (
($params{selectname} !~ /^\d+$/ && (lc $params{selectname} ne lc $name)) ||
($params{selectname} =~ /^\d+$/ && ($params{selectname} != $number))));
}
my %thisparams = %params;
$thisparams{number} = $number;
$thisparams{name} = $name;
$thisparams{gets} = $gets;
$thisparams{misses} = $misses;
$thisparams{sleeps} = $sleeps;
$thisparams{wait_time} = $wait_time;
$thisparams{sumsleeps} = $sumsleeps;
my $latch = DBD::Oracle::Server::Instance::SGA::Latch->new(
%thisparams);
add_latch($latch);
$num_latches++;
}
if (! $num_latches) {
$initerrors = 1;
return undef;
}
}
}
}
sub new {
my $class = shift;
my %params = @_;
my $self = {
handle => $params{handle},
number => $params{number},
name => $params{name},
gets => $params{gets},
misses => $params{misses},
sleeps => $params{sleeps},
wait_time => $params{wait_time},
sumsleeps => $params{sumsleeps},
hitratio => undef,
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
};
bless $self, $class;
$self->init(%params);
return $self;
}
sub init {
my $self = shift;
my %params = @_;
$self->init_nagios();
if ($params{mode} =~
/server::instance::sga::latch::hitratio/) {
if (! defined $self->{gets}) {
$self->add_nagios_critical(
sprintf "unable to get sga latches %s", $self->{name});
} else {
$params{differenciator} = lc $self->{name}.$self->{number};
$self->valdiff(\%params, qw(gets misses));
$self->{hitratio} = $self->{delta_gets} ?
100 * ($self->{delta_gets} - $self->{delta_misses}) / $self->{delta_gets} : 100;
}
} elsif (($params{mode} =~ /server::instance::sga::latch::contention/) ||
($params{mode} =~ /server::instance::sga::latch::waiting/)) {
if (! defined $self->{gets}) {
$self->add_nagios_critical(
sprintf "unable to get sga latches %s", $self->{name});
} else {
$params{differenciator} = lc $self->{name}.$self->{number};
$self->valdiff(\%params, qw(gets sleeps misses wait_time sumsleeps));
# latch contention
$self->{contention} = $self->{delta_gets} ?
100 * $self->{delta_misses} / $self->{delta_gets} : 0;
# latch percent of sleep during the elapsed time
$self->{sleep_share} = $self->{delta_wait_time} ?
((100 * $self->{wait_time}) / 1000) / $self->{delta_timestamp} : 0;
}
}
}
sub nagios {
my $self = shift;
my %params = @_;
if (! $self->{nagios_level}) {
if ($params{mode} =~
/server::instance::sga::latch::hitratio/) {
$self->add_nagios(
$self->check_thresholds($self->{hitratio}, "98:", "95:"),
sprintf "SGA latches hit ratio %.2f%%", $self->{hitratio});
$self->add_perfdata(sprintf "sga_latches_hit_ratio=%.2f%%;%s;%s",
$self->{hitratio}, $self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~
/server::instance::sga::latch::contention/) {
$self->add_nagios(
$self->check_thresholds($self->{contention}, "1", "2"),
sprintf "SGA latch %s (#%d) contention %.2f%%",
$self->{name}, $self->{number}, $self->{contention});
$self->add_perfdata(sprintf "'latch_%d_contention'=%.2f%%;%s;%s",
$self->{number}, $self->{contention}, $self->{warningrange}, $self->{criticalrange});
$self->add_perfdata(sprintf "'latch_%d_gets'=%u",
$self->{number}, $self->{delta_gets});
} elsif ($params{mode} =~
/server::instance::sga::latch::waiting/) {
$self->add_nagios(
$self->check_thresholds($self->{sleep_share}, "0.1", "1"),
sprintf "SGA latch %s (#%d) sleeping %.6f%% of the time",
$self->{name}, $self->{number}, $self->{sleep_share});
$self->add_perfdata(sprintf "'latch_%d_sleep_share'=%.6f%%;%s;%s;0;100",
$self->{number}, $self->{sleep_share}, $self->{warningrange}, $self->{criticalrange});
}
}
}
package DBD::Oracle::Server::Instance::SGA;
use strict;
our @ISA = qw(DBD::Oracle::Server::Instance);
sub new {
my $class = shift;
my %params = @_;
my $self = {
handle => $params{handle},
data_buffer => undef,
shared_pool => undef,
latches => undef,
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
};
bless $self, $class;
$self->init(%params);
return $self;
}
sub init {
my $self = shift;
my %params = @_;
$self->init_nagios();
if ($params{mode} =~ /server::instance::sga::databuffer/) {
$self->{data_buffer} = DBD::Oracle::Server::Instance::SGA::DataBuffer->new(
%params);
} elsif ($params{mode} =~ /server::instance::sga::sharedpool/) {
$self->{shared_pool} = DBD::Oracle::Server::Instance::SGA::SharedPool->new(
%params);
} elsif ($params{mode} =~ /server::instance::sga::latch/) {
DBD::Oracle::Server::Instance::SGA::Latch::init_latches(%params);
if (my @latches =
DBD::Oracle::Server::Instance::SGA::Latch::return_latches(%params)) {
$self->{latches} = \@latches;
} else {
$self->add_nagios_critical("unable to aquire latch info");
}
} elsif ($params{mode} =~ /server::instance::sga::redolog/) {
$self->{redo_log_buffer} =
DBD::Oracle::Server::Instance::SGA::RedoLogBuffer->new(%params);
} elsif ($params{mode} =~ /server::instance::sga::rollbacksegments/) {
$self->{rollback_segments} =
DBD::Oracle::Server::Instance::SGA::RollbackSegments->new(%params);
}
}
sub nagios {
my $self = shift;
my %params = @_;
if ($params{mode} =~ /server::instance::sga::databuffer/) {
$self->{data_buffer}->nagios(%params);
$self->merge_nagios($self->{data_buffer});
} elsif ($params{mode} =~ /server::instance::sga::sharedpool/) {
$self->{shared_pool}->nagios(%params);
$self->merge_nagios($self->{shared_pool});
} elsif ($params{mode} =~ /server::instance::sga::latch::hitratio/) {
if (! $self->{nagios_level}) {
my $hitratio = 0;
foreach (@{$self->{latches}}) {
$hitratio = $hitratio + $_->{hitratio};
}
$hitratio = $hitratio / scalar(@{$self->{latches}});
$self->add_nagios(
$self->check_thresholds($hitratio, "98:", "95:"),
sprintf "SGA latches hit ratio %.2f%%", $hitratio);
$self->add_perfdata(sprintf "sga_latches_hit_ratio=%.2f%%;%s;%s",
$hitratio, $self->{warningrange}, $self->{criticalrange});
}
} elsif ($params{mode} =~ /server::instance::sga::latch::listlatches/) {
foreach (sort { $a->{number} <=> $b->{number} } @{$self->{latches}}) {
printf "%03d %s\n", $_->{number}, $_->{name};
}
$self->add_nagios_ok("have fun");
} elsif ($params{mode} =~ /server::instance::sga::latch/) {
foreach (@{$self->{latches}}) {
$_->nagios(%params);
$self->merge_nagios($_);
}
} elsif ($params{mode} =~ /server::instance::sga::redologbuffer/) {
$self->{redo_log_buffer}->nagios(%params);
$self->merge_nagios($self->{redo_log_buffer});
} elsif ($params{mode} =~ /server::instance::sga::rollbacksegments/) {
$self->{rollback_segments}->nagios(%params);
$self->merge_nagios($self->{rollback_segments});
}
}
package DBD::Oracle::Server::Instance::PGA;
use strict;
our @ISA = qw(DBD::Oracle::Server::Instance);
sub new {
my $class = shift;
my %params = @_;
my $self = {
handle => $params{handle},
internals => undef,
pgas => [],
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
};
bless $self, $class;
$self->init(%params);
return $self;
}
sub init {
my $self = shift;
my %params = @_;
$self->init_nagios();
if ($params{mode} =~ /server::instance::pga/) {
$self->{internals} =
DBD::Oracle::Server::Instance::PGA::Internals->new(%params);
}
}
sub nagios {
my $self = shift;
my %params = @_;
if ($params{mode} =~ /server::instance::pga/) {
$self->{internals}->nagios(%params);
$self->merge_nagios($self->{internals});
}
}
package DBD::Oracle::Server::Instance::PGA::Internals;
use strict;
our @ISA = qw(DBD::Oracle::Server::Instance::PGA);
my $internals; # singleton, nur ein einziges mal instantiierbar
sub new {
my $class = shift;
my %params = @_;
unless ($internals) {
$internals = {
handle => $params{handle},
in_memory_sorts => undef,
in_disk_sorts => undef,
in_memory_sort_ratio => undef,
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
};
bless($internals, $class);
$internals->init(%params);
}
return($internals);
}
sub init {
my $self = shift;
my %params = @_;
$self->debug("enter init");
$self->init_nagios();
if ($params{mode} =~ /server::instance::pga::inmemorysortratio/) {
($self->{in_memory_sorts}, $self->{in_disk_sorts}) =
$self->{handle}->fetchrow_array(q{
SELECT mem.value, dsk.value
FROM v$sysstat mem, v$sysstat dsk
WHERE mem.name='sorts (memory)' AND dsk.name='sorts (disk)'
});
if (! defined $self->{in_memory_sorts}) {
$self->add_nagios_critical("unable to get pga ratio");
} else {
$self->valdiff(\%params, qw(in_memory_sorts in_disk_sorts));
$self->{in_memory_sort_ratio} =
($self->{delta_in_memory_sorts} + $self->{delta_in_disk_sorts}) == 0 ? 100 :
100 * $self->{delta_in_memory_sorts} /
($self->{delta_in_memory_sorts} + $self->{delta_in_disk_sorts});
}
}
}
sub nagios {
my $self = shift;
my %params = @_;
if (! $self->{nagios_level}) {
if ($params{mode} =~ /server::instance::pga::inmemorysortratio/) {
$self->add_nagios(
$self->check_thresholds($self->{in_memory_sort_ratio}, "99:", "90:"),
sprintf "PGA in-memory sort ratio %.2f%%",
$self->{in_memory_sort_ratio});
$self->add_perfdata(sprintf "pga_in_memory_sort_ratio=%.2f%%;%s;%s;0;100",
$self->{in_memory_sort_ratio},
$self->{warningrange}, $self->{criticalrange});
}
}
}
package DBD::Oracle::Server::Instance::Event;
use strict;
our @ISA = qw(DBD::Oracle::Server::Instance);
{
my @events = ();
my $initerrors = undef;
sub add_event {
push(@events, shift);
}
sub return_events {
my %params = @_;
if ($params{mode} =~ /server::instance::event::waits/) {
return reverse
sort { $a->{waits_per_sec} <=> $b->{waits_per_sec} } @events;
} elsif ($params{mode} =~ /server::instance::event::waiting/) {
return reverse
sort { $a->{percent_waited} <=> $b->{percent_waited} } @events;
} else {
return reverse
sort { $a->{name} cmp $b->{name} } @events;
}
}
sub init_events {
my %params = @_;
my $num_events = 0;
my %longnames = ();
if (($params{mode} =~ /server::instance::event::wait/) || #waits, waiting
($params{mode} =~ /server::instance::event::listevents/)) {
my $sql;
my @idlewaits = ();
if (DBD::Oracle::Server::return_first_server()->version_is_minimum("10.x")) {
@idlewaits = map { $_->[0] } $params{handle}->fetchall_array(q{
SELECT name FROM v$event_name WHERE wait_class = 'Idle'
});
} elsif (DBD::Oracle::Server::return_first_server()->version_is_minimum("9.x")) {
@idlewaits = (
'smon timer',
'pmon timer',
'rdbms ipc message',
'Null event',
'parallel query dequeue',
'pipe get',
'client message',
'SQL*Net message to client',
'SQL*Net message from client',
'SQL*Net more data from client',
'dispatcher timer',
'virtual circuit status',
'lock manager wait for remote message',
'PX Idle Wait',
'PX Deq: Execution Msg',
'PX Deq: Table Q Normal',
'wakeup time manager',
'slave wait',
'i/o slave wait',
'jobq slave wait',
'null event',
'gcs remote message',
'gcs for action',
'ges remote message',
'queue messages',
);
}
if ($params{mode} =~ /server::instance::event::listeventsbg/) {
if (DBD::Oracle::Server::return_first_server()->version_is_minimum("10.x")) {
$sql = q{
SELECT e.event_id, e.event, 0, 0, 0, 0 FROM v$session_event e WHERE e.sid IN
(SELECT s.sid FROM v$session s WHERE s.type = 'BACKGROUND') GROUP BY e.event, e.event_id
};
} else {
$sql = q{
SELECT n.event#, e.event, 0, 0, 0, 0 FROM v$session_event e, v$event_name n
WHERE n.name = e.event AND e.sid IN
(SELECT s.sid FROM v$session s WHERE s.type = 'BACKGROUND') GROUP BY e.event, n.event#
};
}
} else {
if (DBD::Oracle::Server::return_first_server()->version_is_minimum("10.x")) {
$sql = q{
SELECT e.event_id, e.name,
NVL(s.total_waits, 0), NVL(s.total_timeouts, 0), NVL(s.time_waited, 0),
NVL(s.time_waited_micro, 0), NVL(s.average_wait, 0)
FROM v$event_name e LEFT JOIN sys.v_$system_event s ON e.name = s.event
};
} else {
$sql = q{
SELECT e.event#, e.name,
NVL(s.total_waits, 0), NVL(s.total_timeouts, 0), NVL(s.time_waited, 0),
NVL(s.time_waited_micro, 0), NVL(s.average_wait, 0)
FROM v$event_name e LEFT JOIN sys.v_$system_event s ON e.name = s.event
};
}
}
my @eventresults = $params{handle}->fetchall_array($sql);
foreach (@eventresults) {
my ($event_no, $name, $total_waits, $total_timeouts,
$time_waited, $time_waited_micro, $average_wait) = @{$_};
$longnames{$name} = "";
}
abbreviate(\%longnames, 2);
foreach (@eventresults) {
my ($event_no, $name, $total_waits, $total_timeouts,
$time_waited, $time_waited_micro, $average_wait) = @{$_};
my $shortname = $longnames{$name}->{abbreviation};
if ($params{regexp}) {
next if $params{selectname} && $name !~ /$params{selectname}/;
} else {
next if ($params{selectname} && (
(($params{selectname} !~ /^\d+$/) &&
(! grep /^$params{selectname}$/, map { $longnames{$_}->{abbreviation} }
keys %longnames) &&
(lc $params{selectname} ne lc $name)) ||
(($params{selectname} !~ /^\d+$/) &&
(grep /^$params{selectname}$/, map { $longnames{$_}->{abbreviation} }
keys %longnames) &&
(lc $params{selectname} ne lc $shortname)) ||
($params{selectname} =~ /^\d+$/ &&
($params{selectname} != $event_no))));
}
my %thisparams = %params;
$thisparams{name} = $name;
$thisparams{shortname} = $shortname;
$thisparams{event_id} = $event_no; # bei > 10.x unbedingt event_id aus db holen
$thisparams{total_waits} = $total_waits;
$thisparams{total_timeouts} = $total_timeouts;
$thisparams{time_waited} = $time_waited;
$thisparams{time_waited_micro} = $time_waited_micro;
$thisparams{average_wait} = $average_wait;
$thisparams{idle} = scalar(grep { lc $name =~ /$_/ } @idlewaits);
my $event = DBD::Oracle::Server::Instance::Event->new(
%thisparams);
add_event($event);
$num_events++;
}
if (! $num_events) {
$initerrors = 1;
return undef;
}
}
}
sub begindiff {
# liefere indices fuer das erste untersch. wort und innerhalb diesem das erste untersch. zeichen
my @names = @_;
my $len = 100;
my $first_diff_word = 0;
my $first_diff_pos = 0;
my $smallest_wordcnt = (sort { $a->{wordcnt} <=> $b->{wordcnt} } @names)[0]->{wordcnt};
foreach my $wordno (0..$smallest_wordcnt-1) {
my $wordequal = 1;
my $refword = @{$names[0]->{words}}[$wordno];
foreach (@names) {
if (@{$_->{words}}[$wordno] ne $refword) {
$wordequal = 0;
}
}
$first_diff_word = $wordno;
if (! $wordequal) {
last;
}
}
my $smallest_wordlen =
length(${(sort { length(${$a->{words}}[$first_diff_word]) <=> length(${$b->{words}}[$first_diff_word]) } @names)[0]->{words}}[$first_diff_word]);
foreach my $posno (0..$smallest_wordlen-1) {
my $posequal = 1;
my $refpos = substr(@{$names[0]->{words}}[$first_diff_word], $posno, 1);
foreach (@names) {
if (substr(@{$_->{words}}[$first_diff_word], $posno, 1) ne $refpos) {
$posequal = 0;
}
}
$first_diff_pos = $posno;
if (! $posequal) {
last;
}
}
return ($first_diff_word, $first_diff_pos);
}
sub abbreviate {
#
# => zeiger auf hash, dessen keys lange namen sind
# <= gleicher hash mit ausgefuellten eindeutigen values
#
my $names = shift;
my %done = ();
my $collisions = {};
foreach my $long (keys %{$names}) {
# erstmal das noetige werkzeug schmieden
# und kurzbezeichnungen aus jeweils zwei zeichen bilden
$names->{$long} = {};
$names->{$long}->{words} = [
map { lc }
map { my $x = $_; $x =~ s/[()\/\-]//g; $x }
map { /^\-$/ ? () : $_ }
split(/_|\s+/, $long) ];
$names->{$long}->{wordcnt} = scalar (@{$names->{$long}->{words}});
$names->{$long}->{shortwords} = [ map { substr $_, 0, 2 } @{$names->{$long}->{words}} ];
$names->{$long}->{abbreviation} = join("_", @{$names->{$long}->{shortwords}});
$names->{$long}->{unique} = 1;
}
individualize($names, -1, -1);
}
sub individualize {
my $names = shift;
my $delword = shift;
my $delpos = shift;
my %done = ();
my $collisions = {};
if ($delword >= 0 && $delpos >= 0) {
# delpos ist die position mit dem ersten unterschied. kann fuer den kuerzesten string
# schon nicht mehr existieren.
map {
if (length(${$names->{$_}->{words}}[$delword]) > 2) {
if (length(${$names->{$_}->{words}}[$delword]) == $delpos) {
${$names->{$_}->{shortwords}}[$delword] =
substr(${$names->{$_}->{words}}[$delword], 0, 2)
} else {
${$names->{$_}->{shortwords}}[$delword] =
substr(${$names->{$_}->{words}}[$delword], 0, 1).
substr(${$names->{$_}->{words}}[$delword], $delpos);
}
}
} keys %{$names};
}
map { $names->{$_}->{abbreviation} = join("_", @{$names->{$_}->{shortwords}}) } keys %{$names};
map { $done{$names->{$_}->{abbreviation}}++ } keys %{$names};
map { $names->{$_}->{unique} = $done{$names->{$_}->{abbreviation}} > 1 ? 0 : 1 } keys %{$names};
#
# hash mit abkuerzung als key und array(langnamen, ...) als value.
# diese sind nicht eindeutig und muessen noch geschickter abgekuerzt werden
#
foreach my $collision (map { $names->{$_}->{unique} ? () : $_ } keys %{$names}) {
if (! exists $collisions->{$names->{$collision}->{abbreviation}}) {
$collisions->{$names->{$collision}->{abbreviation}} = [];
}
push(@{$collisions->{$names->{$collision}->{abbreviation}}}, $collision);
}
#
# jeweils gruppen mit gemeinsamer, mehrdeutiger abkuerzung werden nochmals gerechnet
#
foreach my $collision (keys %{$collisions}) {
my $newnames = {};
# hilfestellung, wo es unterschiede gibt
my($wordnum, $posnum) = begindiff(map { $names->{$_} } @{$collisions->{$collision}});
map { $newnames->{$_} =
$names->{$_} } grep { $names->{$_}->{abbreviation} eq $collision } keys %{$names};
individualize($newnames, $wordnum, $posnum);
}
}
}
sub new {
my $class = shift;
my %params = @_;
my $self = {
handle => $params{handle},
name => $params{name},
shortname => $params{shortname},
event_id => $params{event_id}, # > 10.x
total_waits => $params{total_waits},
total_timeouts => $params{total_timeouts},
time_waited => $params{time_waited}, # divide by 100
time_waited_micro => $params{time_waited_micro}, # divide by 1000000
average_wait => $params{average_wait},
idle => $params{idle} || 0,
waits_per_sec => undef,
percent_waited => undef,
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
};
#$self->{name} =~ s/^\s+//;
#$self->{name} =~ s/\s+$//;
bless $self, $class;
$self->init(%params);
return $self;
}
sub init {
my $self = shift;
my %params = @_;
$self->init_nagios();
if ($params{mode} =~ /server::instance::event::wait/) {
if (! defined $self->{total_waits}) {
$self->add_nagios_critical("unable to get event info");
} else {
$params{differenciator} = lc $self->{name};
$self->valdiff(\%params, qw(total_waits total_timeouts time_waited
time_waited_micro average_wait));
$self->{waits_per_sec} =
$self->{delta_total_waits} / $self->{delta_timestamp};
$self->{percent_waited} =
100 * ($self->{delta_time_waited_micro} / 1000000 ) / $self->{delta_timestamp};
}
}
}
sub nagios {
my $self = shift;
my %params = @_;
if (! $self->{nagios_level}) {
if ($params{mode} =~ /server::instance::event::waits/) {
$self->add_nagios(
$self->check_thresholds($self->{waits_per_sec}, "10", "100"),
sprintf "%s : %.6f waits/sec", $self->{name}, $self->{waits_per_sec});
$self->add_perfdata(sprintf "'%s_waits_per_sec'=%.6f;%s;%s",
$self->{name},
$self->{waits_per_sec},
$self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~ /server::instance::event::waiting/) {
$self->add_nagios(
$self->check_thresholds($self->{percent_waited}, "0.1", "0.5"),
sprintf "%s waits %.6f%% of the time", $self->{name}, $self->{percent_waited});
$self->add_perfdata(sprintf "'%s_percent_waited'=%.6f%%;%s;%s",
$self->{name},
$self->{percent_waited},
$self->{warningrange}, $self->{criticalrange});
}
}
}
package DBD::Oracle::Server::Instance::Enqueue;
use strict;
our @ISA = qw(DBD::Oracle::Server::Instance);
{
my @enqueues = ();
my $initerrors = undef;
sub add_enqueue {
push(@enqueues, shift);
}
sub return_enqueues {
return reverse
sort { $a->{name} cmp $b->{name} } @enqueues;
}
sub init_enqueues {
my %params = @_;
my $num_enqueues = 0;
if (($params{mode} =~ /server::instance::enqueue::contention/) ||
($params{mode} =~ /server::instance::enqueue::waiting/) ||
($params{mode} =~ /server::instance::enqueue::listenqueues/)) {
# ora11 PE FP TA DL SR TQ KT PW XR SS SJ SQ IT IA UL WP RR KM
# PD CF SW CT US TD TK JS FS CN DT TS TT JD SE MW AF TL
# PV AS TM TX FB JQ MD TO TH PR RO MR DP WF TB SH RS CU
# AE CI PG IS RT HW DR FU
# ora10 PE FP TA DL SR TQ KT PW XR SS SQ PF IT IA UL WP KM PD
# CF SW CT US TD AG JS DT TS TT CN JD SE MW AF TL PV AS
# TM FB TX JQ MD TO PR RO MR SK DP WF TB SH RS CU AW CI
# PG IS RT HW DR FU
# ora9 CF CI CU DL DP DR DT DX FB HW IA IS IT JD MD MR PE PF
# RO RT SQ SR SS SW TA TD TM TO TS TT TX UL US XR
my @enqueueresults = $params{handle}->fetchall_array(q{
SELECT inst_id, eq_type, total_req#, total_wait#,
succ_req#, failed_req#, cum_wait_time
FROM v$enqueue_stat
});
foreach (@enqueueresults) {
my ($inst_id, $name, $total_requests, $total_waits,
$succeeded_requests, $failed_requests, $cumul_wait_time) = @{$_};
if ($params{regexp}) {
next if $params{selectname} && $name !~ /$params{selectname}/;
} else {
next if $params{selectname} && lc $params{selectname} ne lc $name;
}
my %thisparams = %params;
$thisparams{name} = $name;
$thisparams{total_requests} = $total_requests;
$thisparams{total_waits} = $total_waits;
$thisparams{succeeded_requests} = $succeeded_requests;
$thisparams{failed_requests} = $failed_requests;
$thisparams{cumul_wait_time} = $cumul_wait_time;
my $enqueue = DBD::Oracle::Server::Instance::Enqueue->new(
%thisparams);
add_enqueue($enqueue);
$num_enqueues++;
}
if (! $num_enqueues) {
$initerrors = 1;
return undef;
}
}
}
}
sub new {
my $class = shift;
my %params = @_;
my $self = {
handle => $params{handle},
name => $params{name},
total_requests => $params{total_requests},
total_waits => $params{total_waits},
succeeded_requests => $params{succeeded_requests},
failed_requests => $params{failed_requests},
cumul_wait_time => $params{cumul_wait_time}, # ! milliseconds
contention => undef,
percent_waited => undef,
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
};
$self->{name} =~ s/^\s+//;
$self->{name} =~ s/\s+$//;
bless $self, $class;
$self->init(%params);
return $self;
}
sub init {
my $self = shift;
my %params = @_;
$self->init_nagios();
if (($params{mode} =~ /server::instance::enqueue::contention/) ||
($params{mode} =~ /server::instance::enqueue::waiting/)) {
$params{differenciator} = lc $self->{name};
$self->valdiff(\%params, qw(total_requests total_waits succeeded_requests
failed_requests cumul_wait_time));
# enqueue contention
$self->{contention} = $self->{delta_total_requests} ?
100 * $self->{delta_total_waits} / $self->{delta_total_requests} : 0;
# enqueue waiting
$self->{percent_waited} = ($self->{delta_cumul_wait_time} /
($self->{delta_timestamp} * 1000)) * 100;
}
}
sub nagios {
my $self = shift;
my %params = @_;
if (! $self->{nagios_level}) {
if ($params{mode} =~ /server::instance::enqueue::contention/) {
$self->add_nagios(
$self->check_thresholds($self->{contention}, "1", "10"),
sprintf "enqueue %s %s: %.2f%% of the requests must wait ",
$self->{name}, $self->longname(), $self->{contention});
$self->add_perfdata(sprintf "'%s_contention'=%.2f%%;%s;%s '%s_requests'=%d '%s_waits'=%d",
$self->{name},
$self->{contention},
$self->{warningrange}, $self->{criticalrange},
$self->{name},
$self->{delta_total_requests},
$self->{name},
$self->{delta_total_waits});
} elsif ($params{mode} =~ /server::instance::enqueue::waiting/) {
$self->add_nagios(
# 1 ms wait in 5 minutes
$self->check_thresholds($self->{percent_waited}, "0.0003333", "0.003333"),
sprintf "enqueue %s %s: waiting %.4f%% of the time",
$self->{name}, $self->longname(), $self->{percent_waited});
$self->add_perfdata(sprintf "'%s_ms_waited'=%d '%s_pct_waited'=%.4f%%;%s;%s",
$self->{name},
$self->{delta_cumul_wait_time},
$self->{name},
$self->{percent_waited},
$self->{warningrange}, $self->{criticalrange});
}
}
}
sub longname {
my $self = shift;
my $abbrev = <
BL, Buffer Cache Management
BR, Backup/Restore
CF, Controlfile Transaction
CI, Cross-instance Call Invocation
CU, Bind Enqueue
DF, Datafile
DL, Direct Loader Index Creation
DM, Database Mount
DR, Distributed Recovery Process
DX, Distributed Transaction
FP, File Object
FS, File Set
HW, High-Water Lock
IN, Instance Number
IR, Instance Recovery
IS, Instance State
IV, Library Cache Invalidation
JI, Enqueue used during AJV snapshot refresh
JQ, Job Queue
KK, Redo Log "Kick"
KO, Multiple Object Checkpoint
L[A-P], Library Cache Lock
LS, Log Start or Switch
MM, Mount Definition
MR, Media Recovery
N[A-Z], Library Cache Pin
PE, ALTER SYSTEM SET PARAMETER = VALUE
PF, Password File
PI, Parallel Slaves
PR, Process Startup
PS, Parallel Slave Synchronization
Q[A-Z], Row Cache
RO, Object Reuse
RT, Redo Thread
RW, Row Wait
SC, System Commit Number
SM, SMON
SN, Sequence Number
SQ, Sequence Number Enqueue
SR, Synchronized Replication
SS, Sort Segment
ST, Space Management Transaction
SV, Sequence Number Value
TA, Transaction Recovery
TC, Thread Checkpoint
TE, Extend Table
TM, DML Enqueue
TO, Temporary Table Object Enqueue
TS, Temporary Segment (also TableSpace)
TT, Temporary Table
TX, Transaction
UL, User-defined Locks
UN, User Name
US, Undo Segment, Serialization
WL, Being Written Redo Log
XA, Instance Attribute Lock
XI, Instance Registration Lock
EOEO
my $descriptions = {};
foreach (split(/\n/, $abbrev)) {
my ($short, $descr) = split /,/;
if ($self->{name} =~ /^$short$/) {
$descr =~ s/^\s+//g;
return $descr;
}
}
return "";
}
package DBD::Oracle::Server::Instance::Sysstat;
use strict;
our @ISA = qw(DBD::Oracle::Server::Instance);
{
my @sysstats = ();
my $initerrors = undef;
sub add_sysstat {
push(@sysstats, shift);
}
sub return_sysstats {
return reverse
sort { $a->{name} cmp $b->{name} } @sysstats;
}
sub init_sysstats {
my %params = @_;
my $num_sysstats = 0;
my %longnames = ();
if (($params{mode} =~ /server::instance::sysstat::rate/) ||
($params{mode} =~ /server::instance::sysstat::listsysstats/)) {
my @sysstatresults = $params{handle}->fetchall_array(q{
SELECT statistic#, name, class, value FROM v$sysstat
});
foreach (@sysstatresults) {
my ($number, $name, $class, $value) = @{$_};
if ($params{regexp}) {
next if $params{selectname} && $name !~ /$params{selectname}/;
} else {
next if ($params{selectname} && (
($params{selectname} !~ /^\d+$/ && (lc $params{selectname} ne lc $name)) ||
($params{selectname} =~ /^\d+$/ && ($params{selectname} != $number))));
}
my %thisparams = %params;
$thisparams{name} = $name;
$thisparams{number} = $number;
$thisparams{class} = $class;
$thisparams{value} = $value;
my $sysstat = DBD::Oracle::Server::Instance::Sysstat->new(
%thisparams);
add_sysstat($sysstat);
$num_sysstats++;
}
if (! $num_sysstats) {
$initerrors = 1;
return undef;
}
}
}
}
sub new {
my $class = shift;
my %params = @_;
my $self = {
handle => $params{handle},
name => $params{name},
number => $params{number},
class => $params{class},
value => $params{value},
rate => $params{rate},
count => $params{count},
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
};
#$self->{name} =~ s/^\s+//;
#$self->{name} =~ s/\s+$//;
bless $self, $class;
$self->init(%params);
return $self;
}
sub init {
my $self = shift;
my %params = @_;
$self->init_nagios();
if ($params{mode} =~ /server::instance::sysstat::rate/) {
$params{differenciator} = lc $self->{name};
$self->valdiff(\%params, qw(value));
$self->{rate} = $self->{delta_value} / $self->{delta_timestamp};
}
}
sub nagios {
my $self = shift;
my %params = @_;
if (! $self->{nagios_level}) {
if ($params{mode} =~ /server::instance::sysstat::rate/) {
$self->add_nagios(
$self->check_thresholds($self->{rate}, "10", "100"),
sprintf "%.6f %s/sec", $self->{rate}, $self->{name});
$self->add_perfdata(sprintf "'%s_per_sec'=%.6f;%s;%s",
$self->{name},
$self->{rate},
$self->{warningrange}, $self->{criticalrange});
$self->add_perfdata(sprintf "'%s'=%u",
$self->{name},
$self->{delta_value});
}
}
}
package DBD::Oracle::Server::Instance;
use strict;
our @ISA = qw(DBD::Oracle::Server);
sub new {
my $class = shift;
my %params = @_;
my $self = {
handle => $params{handle},
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
sga => undef,
processes => {},
events => [],
enqueues => [],
};
bless $self, $class;
$self->init(%params);
return $self;
}
sub init {
my $self = shift;
my %params = @_;
$self->init_nagios();
if ($params{mode} =~ /server::instance::sga/) {
$self->{sga} = DBD::Oracle::Server::Instance::SGA->new(%params);
} elsif ($params{mode} =~ /server::instance::pga/) {
$self->{pga} = DBD::Oracle::Server::Instance::PGA->new(%params);
} elsif ($params{mode} =~ /server::instance::sysstat/) {
DBD::Oracle::Server::Instance::Sysstat::init_sysstats(%params);
if (my @sysstats =
DBD::Oracle::Server::Instance::Sysstat::return_sysstats(%params)) {
$self->{sysstats} = \@sysstats;
} else {
$self->add_nagios_critical("unable to aquire sysstats info");
}
} elsif ($params{mode} =~ /server::instance::event/) {
DBD::Oracle::Server::Instance::Event::init_events(%params);
if (my @events =
DBD::Oracle::Server::Instance::Event::return_events(%params)) {
$self->{events} = \@events;
} else {
$self->add_nagios_critical("unable to aquire event info");
}
} elsif ($params{mode} =~ /server::instance::enqueue/) {
DBD::Oracle::Server::Instance::Enqueue::init_enqueues(%params);
if (my @enqueues =
DBD::Oracle::Server::Instance::Enqueue::return_enqueues(%params)) {
$self->{enqueues} = \@enqueues;
} else {
$self->add_nagios_critical("unable to aquire enqueue info");
}
} elsif ($params{mode} =~ /server::instance::connectedusers/) {
$self->{connected_users} = $self->{handle}->fetchrow_array(q{
SELECT ROUND (a.used_process / b.max_process * 100)
FROM (SELECT COUNT (*) used_process FROM v$process) a,
(SELECT VALUE max_process
FROM v$parameter
WHERE name = 'processes') b
WHERE ROWNUM = 1
});
} elsif ($params{mode} =~ /server::instance::blockingsessions/) {
$self->{blocking_sessions} = $self->{handle}->fetchrow_array(q{
SELECT COUNT(*) FROM DBA_BLOCKERS
});
} elsif ($params{mode} =~ /server::instance::sessionnum/) {
$self->{session_num} = $self->{handle}->fetchrow_array(q{
SELECT COUNT(*) FROM v$session
});
} elsif ($params{mode} =~ /server::instance::locktimes/) {
$self->{lock_times} = $self->{handle}->fetchrow_array(q{
SELECT /*+ rule */ MAX (ctime) FROM v$lock WHERE TYPE = 'TX'
});
} elsif ($params{mode} =~ /server::instance::locknumbers/) {
$self->{lock_numbers} = $self->{handle}->fetchrow_array(q{
SELECT /*+ rule */ COUNT (*) FROM v$lock WHERE TYPE = 'TX' AND ctime > 5
});
} elsif ($params{mode} =~ /server::instance::longtransactions/) {
$self->{long_transactions} = $self->{handle}->fetchrow_array(q{
SELECT MAX (last_call_et)
FROM v$session
WHERE status = 'ACTIVE'
AND TYPE = 'USER'
AND username NOT IN ('DBVIEW')
AND sid NOT IN (SELECT sid FROM dba_jobs_running)
AND sid NOT IN (SELECT session_id FROM dba_scheduler_running_jobs)
});
} elsif ($params{mode} =~ /server::instance::usedspaces/) {
$self->{used_spaces} = $self->{handle}->fetchrow_array(q{
select round(sum(bytes)/1024/1024/1024,0) from dba_segments
});
}
}
sub nagios {
my $self = shift;
my %params = @_;
if ($params{mode} =~ /server::instance::sga/) {
$self->{sga}->nagios(%params);
$self->merge_nagios($self->{sga});
} elsif ($params{mode} =~ /server::instance::pga/) {
$self->{pga}->nagios(%params);
$self->merge_nagios($self->{pga});
} elsif ($params{mode} =~ /server::instance::event::listevents/) {
foreach (sort { $a->{name} cmp $b->{name} } @{$self->{events}}) {
printf "%10u%s %s %s\n", $_->{event_id}, $_->{idle} ? '*' : '', $_->{shortname}, $_->{name};
}
$self->add_nagios_ok("have fun");
} elsif ($params{mode} =~ /server::instance::event/) {
foreach (@{$self->{events}}) {
$_->nagios(%params);
$self->merge_nagios($_);
}
if (! $self->{nagios_level} && ! $params{selectname}) {
$self->add_nagios_ok("no wait problems");
}
} elsif ($params{mode} =~ /server::instance::sysstat::listsysstat/) {
foreach (sort { $a->{name} cmp $b->{name} } @{$self->{sysstats}}) {
printf "%10d %s\n", $_->{number}, $_->{name};
}
$self->add_nagios_ok("have fun");
} elsif ($params{mode} =~ /server::instance::sysstat/) {
foreach (@{$self->{sysstats}}) {
$_->nagios(%params);
$self->merge_nagios($_);
}
if (! $self->{nagios_level} && ! $params{selectname}) {
$self->add_nagios_ok("no wait problems");
}
} elsif ($params{mode} =~ /server::instance::enqueue::listenqueues/) {
foreach (sort { $a->{name} cmp $b->{name} } @{$self->{enqueues}}) {
printf "%s\n", $_->{name};
}
$self->add_nagios_ok("have fun");
} elsif ($params{mode} =~ /server::instance::enqueue/) {
foreach (@{$self->{enqueues}}) {
$_->nagios(%params);
$self->merge_nagios($_);
}
if (! $self->{nagios_level} && ! $params{selectname}) {
$self->add_nagios_ok("no enqueue problem");
}
} elsif ($params{mode} =~ /server::instance::connectedusers/) {
$self->add_nagios(
$self->check_thresholds($self->{connected_users}, 70, 90),
sprintf "%d%% processes used",
$self->{connected_users});
$self->add_perfdata(sprintf "processes_used=%d%%;%d%%;%d%%",
$self->{connected_users},
$self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~ /server::instance::blockingsessions/) {
$self->add_nagios(
$self->check_thresholds($self->{blocking_sessions}, 1, 3),
sprintf "%d blocking sessions",
$self->{blocking_sessions});
$self->add_perfdata(sprintf "blocking_sessions=%d;%d;%d",
$self->{blocking_sessions},
$self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~ /server::instance::sessionnum/) {
$self->add_nagios(
$self->check_thresholds($self->{session_num}, 1, 3),
sprintf "%d session num",
$self->{session_num});
$self->add_perfdata(sprintf "session_num=%d;%d;%d",
$self->{session_num},
$self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~ /server::instance::locktimes/) {
$self->add_nagios(
$self->check_thresholds($self->{lock_times}, 7200, 14400),
sprintf "%d second lock times",
$self->{lock_times});
$self->add_perfdata(sprintf "lock_times=%d;%d;%d",
$self->{lock_times},
$self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~ /server::instance::locknumbers/) {
$self->add_nagios(
$self->check_thresholds($self->{lock_numbers}, 20, 30),
sprintf "%d lock numbers",
$self->{lock_numbers});
$self->add_perfdata(sprintf "lock_numbers=%d;%d;%d",
$self->{lock_numbers},
$self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~ /server::instance::longtransactions/) {
$self->add_nagios(
$self->check_thresholds($self->{long_transactions}, 7200, 14400),
sprintf "%d second long transactions",
$self->{long_transactions});
$self->add_perfdata(sprintf "long_transactions=%d;%d;%d",
$self->{long_transactions},
$self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~ /server::instance::usedspaces/) {
$self->add_nagios(
$self->check_thresholds($self->{used_spaces}, 10000, 20000),
sprintf "%d GB space has been used",
$self->{used_spaces});
$self->add_perfdata(sprintf "used_spaces=%dGB;%dGB;%dGB",
$self->{used_spaces},
$self->{warningrange}, $self->{criticalrange});
}
}
package DBD::Oracle::Server::Database::Tablespace::Datafile;
use strict;
use File::Basename;
our @ISA = qw(DBD::Oracle::Server::Database::Tablespace);
{
my @datafiles = ();
my $initerrors = undef;
sub add_datafile {
push(@datafiles, shift);
}
sub return_datafiles {
return reverse
sort { $a->{name} cmp $b->{name} } @datafiles;
}
sub clear_datafiles {
@datafiles = ();
}
sub init_datafiles {
my %params = @_;
my $num_datafiles = 0;
if (($params{mode} =~ /server::database::tablespace::datafile::iotraffic/) ||
($params{mode} =~ /server::database::tablespace::datafile::listdatafiles/)) {
# negative values can occur
# column datafile format a30
my @datafileresults = $params{handle}->fetchall_array(q{
SELECT
name datafile, phyrds reads, phywrts writes
FROM
v$datafile a, v$filestat b
WHERE
a.file# = b.file#
UNION
SELECT
name datafile, phyrds reads, phywrts writes
FROM
v$tempfile a, v$tempstat b
WHERE
a.file# = b.file#
});
if (DBD::Oracle::Server::return_first_server()->windows_server()) {
fileparse_set_fstype("MSWin32");
}
foreach (@datafileresults) {
my ($name, $phyrds, $phywrts) = @{$_};
if ($params{regexp}) {
next if $params{selectname} &&
(($name !~ /$params{selectname}/) &&
(basename($name) !~ /$params{selectname}/));
} else {
next if $params{selectname} &&
((lc $params{selectname} ne lc $name) &&
(lc $params{selectname} ne lc basename($name)));
}
my %thisparams = %params;
$thisparams{path} = $name;
$thisparams{name} = basename($name);
$thisparams{phyrds} = $phyrds;
$thisparams{phywrts} = $phywrts;
my $datafile =
DBD::Oracle::Server::Database::Tablespace::Datafile->new(
%thisparams);
add_datafile($datafile);
$num_datafiles++;
}
} elsif ($params{mode} =~ /server::database::tablespace::iobalance/) {
my $sql = q{
-- SELECT REGEXP_REPLACE(file_name,'^.*.\/.*.\/', '') file_name,
SELECT file_name,
SUM(phyrds), SUM(phywrts)
FROM dba_temp_files, v$filestat
WHERE tablespace_name = UPPER(?)
AND file_id=file# GROUP BY tablespace_name, file_name
UNION
-- SELECT REGEXP_REPLACE(file_name,'^.*.\/.*.\/', '') file_name,
SELECT file_name,
SUM(phyrds), SUM(phywrts)
FROM dba_data_files, v$filestat
WHERE tablespace_name = UPPER(?)
AND file_id=file# GROUP BY tablespace_name, file_name };
if (! DBD::Oracle::Server::return_first_server()->version_is_minimum("9.2.0.3")) {
# bug 2436600
$sql = q{
-- SELECT REGEXP_REPLACE(file_name,'^.*.\/.*.\/', '') file_name,
SELECT file_name,
SUM(phyrds), SUM(phywrts)
FROM dba_data_files, v$filestat
WHERE tablespace_name = UPPER(?)
AND file_id=file# GROUP BY tablespace_name, file_name };
}
my @datafileresults = $params{handle}->fetchall_array($sql, $params{selectname}, $params{selectname});
if (DBD::Oracle::Server::return_first_server()->windows_server()) {
fileparse_set_fstype("MSWin32");
}
foreach (@datafileresults) {
my ($name, $phyrds, $phywrts) = @{$_};
my %thisparams = %params;
$thisparams{path} = $name;
$thisparams{name} = basename($name);
$thisparams{phyrds} = $phyrds;
$thisparams{phywrts} = $phywrts;
my $datafile =
DBD::Oracle::Server::Database::Tablespace::Datafile->new(
%thisparams);
add_datafile($datafile);
$num_datafiles++;
}
if (! $num_datafiles) {
$initerrors = 1;
return undef;
}
}
}
}
sub new {
my $class = shift;
my %params = @_;
my $self = {
handle => $params{handle},
path => $params{path},
name => $params{name},
phyrds => $params{phyrds},
phywrts => $params{phywrts},
io_total => undef,
io_total_per_sec => undef,
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
};
bless $self, $class;
$self->init(%params);
return $self;
}
sub init {
my $self = shift;
my %params = @_;
$self->init_nagios();
if ($params{mode} =~ /server::database::tablespace::iobalance/) {
if (! defined $self->{phyrds}) {
$self->add_nagios_critical(sprintf "unable to read datafile io %s", $@);
} else {
$params{differenciator} = $self->{path};
$self->valdiff(\%params, qw(phyrds phywrts));
$self->{io_total} = $self->{delta_phyrds} + $self->{delta_phywrts};
}
} elsif ($params{mode} =~ /server::database::tablespace::datafile::iotraffic/) {
if (! defined $self->{phyrds}) {
$self->add_nagios_critical(sprintf "unable to read datafile io %s", $@);
} else {
$params{differenciator} = $self->{path};
$self->valdiff(\%params, qw(phyrds phywrts));
$self->{io_total_per_sec} = ($self->{delta_phyrds} + $self->{delta_phywrts}) /
$self->{delta_timestamp};
}
}
}
sub nagios {
my $self = shift;
my %params = @_;
if (! $self->{nagios_level}) {
if ($params{mode} =~ /server::database::tablespace::datafile::iotraffic/) {
$self->add_nagios(
$self->check_thresholds($self->{io_total_per_sec}, "1000", "5000"),
sprintf ("%s: %.2f IO Operations per Second",
$self->{name}, $self->{io_total_per_sec}));
$self->add_perfdata(sprintf "'dbf_%s_io_total_per_sec'=%.2f;%d;%d",
$self->{name}, $self->{io_total_per_sec},
$self->{warningrange}, $self->{criticalrange});
}
}
}
package DBD::Oracle::Server::Database::Tablespace::Segment;
use strict;
our @ISA = qw(DBD::Oracle::Server::Database::Tablespace);
{
my @segments = ();
my $initerrors = undef;
sub add_segment {
push(@segments, shift);
}
sub return_segments {
return reverse
sort { $a->{name} cmp $b->{name} } @segments;
}
sub clear_segments {
@segments = ();
}
sub init_segments {
my %params = @_;
my $num_segments = 0;
if (($params{mode} =~
/server::database::tablespace::segment::top10logicalreads/) ||
($params{mode} =~
/server::database::tablespace::segment::top10physicalreads/) ||
($params{mode} =~
/server::database::tablespace::segment::top10bufferbusywaits/) ||
($params{mode} =~
/server::database::tablespace::segment::top10rowlockwaits/)) {
my %thisparams = %params;
$thisparams{name} = "dummy_segment";
my $segment = DBD::Oracle::Server::Database::Tablespace::Segment->new(
%thisparams);
add_segment($segment);
$num_segments++;
} elsif ($params{mode} =~
/server::database::tablespace::segment::extendspace/) {
my @tablespaceresult = $params{handle}->fetchall_array(q{
SELECT /*+ RULE */
-- tablespace, segment, extent
-- aber dadurch, dass nur das letzte extent selektiert wird
-- werden praktisch nur tablespace und segmente ausgegeben
b.tablespace_name "Tablespace",
b.segment_type "Type",
SUBSTR(ext.owner||'.'||ext.segment_name,1,50) "Object Name",
DECODE(freespace.extent_management,
'DICTIONARY', DECODE(b.extents,
1, b.next_extent, ext.bytes * (1 + b.pct_increase / 100)),
'LOCAL', DECODE(freespace.allocation_type,
'UNIFORM', freespace.initial_extent,
'SYSTEM', ext.bytes)
) "Required Extent",
freespace.largest "MaxAvail"
FROM
-- dba_segments b,
-- dba_extents ext,
(
SELECT
owner, segment_type, segment_name, extents, pct_increase,
next_extent, tablespace_name
FROM
dba_segments
WHERE
tablespace_name = ?
) b,
(
SELECT
owner, segment_type, segment_name, extent_id, bytes,
tablespace_name
FROM
dba_extents
WHERE
tablespace_name = ?
) ext,
(
-- dictionary/local, uniform/system, initial, next
-- und der groesste freie extent pro tablespace
SELECT
b.tablespace_name,
b.extent_management,
b.allocation_type,
b.initial_extent,
b.next_extent,
max(a.bytes) largest
FROM
dba_free_space a,
dba_tablespaces b
WHERE
b.tablespace_name = a.tablespace_name
AND
b.status = 'ONLINE'
GROUP BY
b.tablespace_name,
b.extent_management,
b.allocation_type,
b.initial_extent,
b.next_extent
) freespace
WHERE
b.owner = ext.owner
AND
b.segment_type = ext.segment_type
AND
b.segment_name = ext.segment_name
AND
b.tablespace_name = ext.tablespace_name
AND
-- so landet nur das jeweils letzte extent im ergebnis
(b.extents - 1) = ext.extent_id
AND
b.tablespace_name = freespace.tablespace_name
AND
freespace.tablespace_name = ?
ORDER BY
b.tablespace_name,
b.segment_type,
b.segment_name
}, $params{tablespace}, $params{tablespace}, $params{tablespace});
foreach (@tablespaceresult) {
my ($tablespace_name, $segment_type, $object_name,
$required_for_next_extent, $largest_free) = @{$_};
my %thisparams = %params;
$thisparams{name} = $object_name;
$thisparams{segment_type} = $segment_type;
$thisparams{required_for_next_extent} = $required_for_next_extent;
$thisparams{largest_free} = $largest_free;
my $segment = DBD::Oracle::Server::Database::Tablespace::Segment->new(
%thisparams);
add_segment($segment);
$num_segments++;
}
}
if (! $num_segments) {
$initerrors = 1;
return undef;
}
}
}
sub new {
my $class = shift;
my %params = @_;
my $self = {
handle => $params{handle},
name => $params{name},
segment_type => $params{segment_type},
required_for_next_extent => $params{required_for_next_extent},
largest_free => $params{largest_free},
num_users_among_top10logicalreads => undef,
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
};
bless $self, $class;
$self->init(%params);
return $self;
}
sub init {
my $self = shift;
my %params = @_;
$self->init_nagios();
if (($params{mode} =~
/server::database::tablespace::segment::top10logicalreads/) ||
($params{mode} =~
/server::database::tablespace::segment::top10physicalreads/) ||
($params{mode} =~
/server::database::tablespace::segment::top10bufferbusywaits/) ||
($params{mode} =~
/server::database::tablespace::segment::top10rowlockwaits/)) {
my $mode = (split(/::/, $params{mode}))[4];
## -- SELECT owner, object_name, object_type, value, statistic_name
my $sql = q{
SELECT COUNT(*)
FROM (select DO.owner, DO.object_name, DO.object_type, SS.value,
SS.statistic_name, row_number () over (order by value desc) RN
FROM dba_objects DO, v$segstat SS
WHERE DO.object_id = SS.obj#
AND statistic_name = ?)
WHERE RN <= 10
AND owner not in
('CTXSYS', 'DBSNMP', 'MDDATA', 'MDSYS', 'DMSYS', 'OLAPSYS',
'ORDPLUGINS', 'ORDSYS', 'OUTLN', 'SI_INFORMTN_SCHEMA',
'SYS', 'SYSMAN', 'SYSTEM')
};
$sql = q{
select DO.owner, DO.object_name, DO.object_type, SS.value,
SS.statistic_name
FROM dba_objects DO, v$segstat SS
WHERE DO.object_id = SS.obj#
AND statistic_name = ?
};
my $statname = {
top10logicalreads => "logical reads",
top10physicalreads => "physical reads",
top10bufferbusywaits => "buffer busy waits",
top10rowlockwaits => "row lock waits",
}->{$mode};
#$self->{"num_users_among_".$mode} =
# $self->{handle}->fetchrow_array($sql, $statname);
# faster version
# fetch everything
my @sortedsessions = reverse sort { $a->[3] <=> $b->[3] } $self->{handle}->fetchall_array($sql, $statname);
if (scalar(@sortedsessions) > 10) {
@sortedsessions = (@sortedsessions)[0..9];
}
my @usersessions = map { $_->[0] !~ /^(CTXSYS|DBSNMP|MDDATA|MDSYS|DMSYS|OLAPSYS|ORDPLUGINS|ORDSYS|OUTLN|SI_INFORMTN_SCHEMA|SYS|SYSMAN|SYSTEM)$/ ? $_ : () } @sortedsessions;
$self->{"num_users_among_".$mode} = scalar(@usersessions);
if (scalar(@sortedsessions) == 0) {
#if (! defined $self->{"num_users_among_".$mode}) {
$self->add_nagios_critical(sprintf "unable to read top10: %s", $@);
}
}
}
sub nagios {
my $self = shift;
my %params = @_;
if (! $self->{nagios_level}) {
if (($params{mode} =~
/server::database::tablespace::segment::top10logicalreads/) ||
($params{mode} =~
/server::database::tablespace::segment::top10physicalreads/) ||
($params{mode} =~
/server::database::tablespace::segment::top10bufferbusywaits/) ||
($params{mode} =~
/server::database::tablespace::segment::top10rowlockwaits/)) {
my $mode = (split(/::/, $params{mode}))[4];
my $statname = {
top10logicalreads => "logical reads",
top10physicalreads => "physical reads",
top10bufferbusywaits => "buffer busy waits",
top10rowlockwaits => "row lock waits",
}->{$mode};
$self->add_nagios(
$self->check_thresholds(
$self->{"num_users_among_".$mode}, "1", "9"),
sprintf "%d user processes among the top10 %s",
$self->{"num_users_among_".$mode}, $statname);
$statname =~ s/\s/_/g;
$self->add_perfdata(sprintf "users_among_top10_%s=%d;%d;%d",
$statname, $self->{"num_users_among_".$mode},
$self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~
/server::database::tablespace::segment::extendspace/) {
if ($self->{required_for_next_extent} > $self->{largest_free}) {
$self->add_nagios_critical(
sprintf "segment %s cannot extend", $self->{name});
}
}
}
}
package DBD::Oracle::Server::Database::Tablespace;
use strict;
our @ISA = qw(DBD::Oracle::Server::Database);
{
my @tablespaces = ();
my $initerrors = undef;
sub add_tablespace {
push(@tablespaces, shift);
}
sub return_tablespaces {
return reverse
sort { $a->{name} cmp $b->{name} } @tablespaces;
}
sub init_tablespaces {
my %params = @_;
my $num_tablespaces = 0;
if (($params{mode} =~ /server::database::tablespace::usage/) ||
($params{mode} =~ /server::database::tablespace::free/) ||
($params{mode} =~ /server::database::tablespace::remainingfreetime/) ||
($params{mode} =~ /server::database::tablespace::listtablespaces/)) {
my @tablespaceresult = ();
if (DBD::Oracle::Server::return_first_server()->version_is_minimum("9.x")) {
@tablespaceresult = $params{handle}->fetchall_array(q{
SELECT
a.tablespace_name "Tablespace",
b.status "Status",
b.contents "Type",
b.extent_management "Extent Mgmt",
a.bytes bytes,
a.maxbytes bytes_max,
c.bytes_free bytes_free
FROM
(
-- belegter und maximal verfuegbarer platz pro datafile
-- nach tablespacenamen zusammengefasst
-- => bytes
-- => maxbytes
SELECT
a.tablespace_name,
SUM(a.bytes) bytes,
SUM(DECODE(a.autoextensible, 'YES', a.maxbytes, 'NO', a.bytes)) maxbytes
FROM
dba_data_files a
GROUP BY
tablespace_name
) a,
sys.dba_tablespaces b,
(
-- freier platz pro tablespace
-- => bytes_free
SELECT
a.tablespace_name,
SUM(a.bytes) bytes_free
FROM
dba_free_space a
GROUP BY
tablespace_name
) c
WHERE
a.tablespace_name = c.tablespace_name (+)
AND a.tablespace_name = b.tablespace_name
UNION ALL
SELECT
a.tablespace_name "Tablespace",
b.status "Status",
b.contents "Type",
b.extent_management "Extent Mgmt",
sum(a.bytes_free + a.bytes_used) bytes, -- allocated
SUM(DECODE(d.autoextensible, 'YES', d.maxbytes, 'NO', d.bytes)) bytes_max,
SUM(a.bytes_free + a.bytes_used - NVL(c.bytes_used, 0)) bytes_free
FROM
sys.v_$TEMP_SPACE_HEADER a,
sys.dba_tablespaces b,
sys.v_$Temp_extent_pool c,
dba_temp_files d
WHERE
c.file_id(+) = a.file_id
and c.tablespace_name(+) = a.tablespace_name
and d.file_id = a.file_id
and d.tablespace_name = a.tablespace_name
and b.tablespace_name = a.tablespace_name
GROUP BY
a.tablespace_name,
b.status,
b.contents,
b.extent_management,
d.maxbytes
ORDER BY 1
});
} else {
@tablespaceresult = $params{handle}->fetchall_array(q{
SELECT
a.tablespace_name "Tablespace",
b.status "Status",
b.contents "Type",
b.extent_management "Extent Mgmt",
a.bytes bytes,
a.maxbytes bytes_max,
c.bytes_free bytes_free
FROM
(
-- belegter und maximal verfuegbarer platz pro datafile
-- nach tablespacenamen zusammengefasst
-- => bytes
-- => maxbytes
SELECT
a.tablespace_name,
SUM(a.bytes) bytes,
SUM(DECODE(a.autoextensible, 'YES', a.maxbytes, 'NO', a.bytes)) maxbytes
FROM
dba_data_files a
GROUP BY
tablespace_name
) a,
sys.dba_tablespaces b,
(
-- freier platz pro tablespace
-- => bytes_free
SELECT
a.tablespace_name,
SUM(a.bytes) bytes_free
FROM
dba_free_space a
GROUP BY
tablespace_name
) c
WHERE
a.tablespace_name = c.tablespace_name (+)
AND a.tablespace_name = b.tablespace_name
AND (100-(c.bytes_free/a.bytes)*100)>79
UNION ALL
SELECT
a.tablespace_name "Tablespace",
b.status "Status",
b.contents "Type",
b.extent_management "Extent Mgmt",
sum(a.bytes_free + a.bytes_used) bytes, -- allocated
d.maxbytes bytes_max,
SUM(a.bytes_free + a.bytes_used - NVL(c.bytes_used, 0)) bytes_free
FROM
sys.v_$TEMP_SPACE_HEADER a,
sys.dba_tablespaces b,
sys.v_$Temp_extent_pool c,
dba_temp_files d
WHERE
c.file_id(+) = a.file_id
and c.tablespace_name(+) = a.tablespace_name
and d.file_id = a.file_id
and d.tablespace_name = a.tablespace_name
and b.tablespace_name = a.tablespace_name
GROUP BY
a.tablespace_name,
b.status,
b.contents,
b.extent_management,
d.maxbytes
HAVING sum(a.bytes_used)/sum(a.bytes_used+a.bytes_free)>=79
ORDER BY
1
});
}
foreach (@tablespaceresult) {
my ($name, $status, $type, $extentmgmt, $bytes, $bytes_max, $bytes_free) = @{$_};
if ($params{regexp}) {
next if $params{selectname} && $name !~ /$params{selectname}/;
} else {
next if $params{selectname} && lc $params{selectname} ne lc $name;
}
my %thisparams = %params;
$thisparams{name} = $name;
$thisparams{bytes} = $bytes;
$thisparams{bytes_max} = $bytes_max;
$thisparams{bytes_free} = $bytes_free;
$thisparams{status} = lc $status;
$thisparams{type} = lc $type;
$thisparams{extent_management} = lc $extentmgmt;
my $tablespace = DBD::Oracle::Server::Database::Tablespace->new(
%thisparams);
add_tablespace($tablespace);
$num_tablespaces++;
}
if (! $num_tablespaces) {
$initerrors = 1;
return undef;
}
} elsif ($params{mode} =~ /server::database::tablespace::fragmentation/) {
my @tablespaceresult = $params{handle}->fetchall_array(q{
SELECT
tablespace_name,
COUNT(*) free_chunks,
DECODE(
ROUND((max(bytes) / 1024000),2),
NULL,0,
ROUND((MAX(bytes) / 1024000),2)) largest_chunk,
NVL(ROUND(SQRT(MAX(blocks)/SUM(blocks))*(100/SQRT(SQRT(COUNT(blocks)) )),2),
0) fragmentation_index
FROM
sys.dba_free_space
GROUP BY
tablespace_name
ORDER BY
2 DESC, 1
});
foreach (@tablespaceresult) {
my ($name, $free_chunks, $largest_chunk, $fragmentation_index) = @{$_};
if ($params{regexp}) {
next if $params{selectname} && $name !~ /$params{selectname}/;
} else {
next if $params{selectname} && lc $params{selectname} ne lc $name;
}
my %thisparams = %params;
$thisparams{name} = $name;
$thisparams{fsfi} = $fragmentation_index;
my $tablespace = DBD::Oracle::Server::Database::Tablespace->new(
%thisparams);
add_tablespace($tablespace);
$num_tablespaces++;
}
if (! $num_tablespaces) {
$initerrors = 1;
return undef;
}
} elsif ($params{mode} =~ /server::database::tablespace::segment::top10/) {
my %thisparams = %params;
$thisparams{name} = "dummy_segment";
$thisparams{segments} = [];
my $tablespace = DBD::Oracle::Server::Database::Tablespace->new(
%thisparams);
add_tablespace($tablespace);
} elsif ($params{mode} =~
/server::database::tablespace::segment::extendspace/) {
my @tablespaceresult = $params{handle}->fetchall_array(q{
SELECT
tablespace_name, extent_management, allocation_type
FROM
dba_tablespaces
});
foreach (@tablespaceresult) {
my ($name, $extent_management, $allocation_type) = @{$_};
if ($params{regexp}) {
next if $params{selectname} && $name !~ /$params{selectname}/;
} else {
next if $params{selectname} && lc $params{selectname} ne lc $name;
}
my %thisparams = %params;
$thisparams{name} = $name;
$thisparams{extent_management} = $extent_management;
$thisparams{allocation_type} = $allocation_type;
my $tablespace = DBD::Oracle::Server::Database::Tablespace->new(
%thisparams);
add_tablespace($tablespace);
$num_tablespaces++;
}
if (! $num_tablespaces) {
$initerrors = 1;
return undef;
}
} elsif ($params{mode} =~ /server::database::tablespace::datafile/) {
my %thisparams = %params;
$thisparams{name} = "dummy_for_datafiles";
$thisparams{datafiles} = [];
my $tablespace = DBD::Oracle::Server::Database::Tablespace->new(
%thisparams);
add_tablespace($tablespace);
} elsif ($params{mode} =~ /server::database::tablespace::iobalance/) {
my @tablespaceresult = $params{handle}->fetchall_array(q{
SELECT tablespace_name FROM dba_tablespaces
});
foreach (@tablespaceresult) {
my ($name) = @{$_};
if ($params{regexp}) {
next if $params{selectname} && $name !~ /$params{selectname}/;
} else {
next if $params{selectname} && lc $params{selectname} ne lc $name;
}
my %thisparams = %params;
$thisparams{name} = $name;
my $tablespace = DBD::Oracle::Server::Database::Tablespace->new(
%thisparams);
add_tablespace($tablespace);
$num_tablespaces++;
}
if (! $num_tablespaces) {
$initerrors = 1;
return undef;
}
}
}
}
sub new {
my $class = shift;
my %params = @_;
my $self = {
handle => $params{handle},
name => $params{name},
bytes => $params{bytes},
bytes_max => $params{bytes_max},
bytes_free => $params{bytes_free} || 0,
extent_management => $params{extent_management},
type => $params{type},
status => $params{status},
fsfi => $params{fsfi},
segments => [],
datafiles => [],
io_total => 0,
usage_history => [],
extent_management => $params{extent_management},
allocation_type => $params{allocation_type},
largest_free_extent => $params{largest_free_extent},
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
};
bless $self, $class;
$self->init(%params);
return $self;
}
sub init {
my $self = shift;
my %params = @_;
$self->init_nagios();
if ($params{mode} =~ /server::database::tablespace::(usage|free)/) {
if (! defined $self->{bytes_max}) {
$self->{bytes} = 0;
$self->{bytes_max} = 0;
$self->{bytes_free} = 0;
$self->{percent_used} = 0;
$self->{real_bytes_max} = $self->{bytes};
$self->{real_bytes_free} = $self->{bytes_free};
$self->{percent_as_bar} = '____________________';
} else {
# (total - free) / total * 100 = % used
# (used + free - free) / ( used + free)
if ($self->{bytes_max} == 0) {
$self->{percent_used} =
($self->{bytes} - $self->{bytes_free}) / $self->{bytes} * 100;
$self->{real_bytes_max} = $self->{bytes};
$self->{real_bytes_free} = $self->{bytes_free};
} elsif ($self->{bytes_max} > $self->{bytes}) {
$self->{percent_used} =
($self->{bytes} - $self->{bytes_free}) / $self->{bytes_max} * 100;
$self->{real_bytes_max} = $self->{bytes_max};
$self->{real_bytes_free} = $self->{bytes_free} + ($self->{bytes_max} - $self->{bytes});
} else {
# alter tablespace USERS add datafile 'users02.dbf'
# size 5M autoextend on next 200K maxsize 6M;
# bytes = 5M, maxbytes = 6M
# ..... data arriving...until ORA-01652: unable to extend temp segment
# bytes = 6M, maxbytes = 6M
# alter database datafile 5 resize 8M;
# bytes = 8M, maxbytes = 6M
$self->{percent_used} =
($self->{bytes} - $self->{bytes_free}) / $self->{bytes} * 100;
$self->{real_bytes_max} = $self->{bytes};
$self->{real_bytes_free} = $self->{bytes_free};
}
}
$self->{percent_free} = 100 - $self->{percent_used};
my $tlen = 20;
my $len = int((($params{mode} =~ /server::database::tablespace::usage/) ?
$self->{percent_used} : $self->{percent_free} / 100 * $tlen) + 0.5);
$self->{percent_as_bar} = '=' x $len . '_' x ($tlen - $len);
} elsif ($params{mode} =~ /server::database::tablespace::fragmentation/) {
} elsif ($params{mode} =~ /server::database::tablespace::segment::top10/) {
DBD::Oracle::Server::Database::Tablespace::Segment::init_segments(%params);
if (my @segments =
DBD::Oracle::Server::Database::Tablespace::Segment::return_segments()) {
$self->{segments} = \@segments;
} else {
$self->add_nagios_critical("unable to aquire segment info");
}
} elsif ($params{mode} =~ /server::database::tablespace::datafile/) {
DBD::Oracle::Server::Database::Tablespace::Datafile::init_datafiles(%params);
if (my @datafiles =
DBD::Oracle::Server::Database::Tablespace::Datafile::return_datafiles()) {
$self->{datafiles} = \@datafiles;
} else {
$self->add_nagios_critical("unable to aquire datafile info");
}
} elsif ($params{mode} =~ /server::database::tablespace::iobalance/) {
$params{tablespace} = $self->{name};
DBD::Oracle::Server::Database::Tablespace::Datafile::init_datafiles(%params);
if (my @datafiles =
DBD::Oracle::Server::Database::Tablespace::Datafile::return_datafiles()) {
$self->{datafiles} = \@datafiles;
map { $self->{io_total} += $_->{io_total} } @datafiles;
DBD::Oracle::Server::Database::Tablespace::Datafile::clear_datafiles();
} else {
$self->add_nagios_critical("unable to aquire datafile info");
}
} elsif ($params{mode} =~ /server::database::tablespace::segment::extendspace/) {
$params{tablespace} = $self->{name};
DBD::Oracle::Server::Database::Tablespace::Segment::init_segments(%params);
my @segments =
DBD::Oracle::Server::Database::Tablespace::Segment::return_segments();
$self->{segments} = \@segments;
DBD::Oracle::Server::Database::Tablespace::Segment::clear_segments();
} elsif ($params{mode} =~ /server::database::tablespace::remainingfreetime/) {
# load historical data
# calculate slope, intercept (go back periods * interval)
# calculate remaining time
$self->{percent_used} = $self->{bytes_max} == 0 ?
($self->{bytes} - $self->{bytes_free}) / $self->{bytes} * 100 :
($self->{bytes} - $self->{bytes_free}) / $self->{bytes_max} * 100;
$self->{usage_history} = $self->load_state( %params ) || [];
my $now = time;
if (scalar(@{$self->{usage_history}})) {
$self->trace(sprintf "loaded %d data sets from %s - %s",
scalar(@{$self->{usage_history}}),
scalar localtime((@{$self->{usage_history}})[0]->[0]),
scalar localtime($now));
# only data sets with valid usage. only newer than 91 days
$self->{usage_history} =
[ grep { defined $_->[1] && ($now - $_->[0]) < 7862400 } @{$self->{usage_history}} ];
$self->trace(sprintf "trimmed to %d data sets from %s - %s",
scalar(@{$self->{usage_history}}),
scalar localtime((@{$self->{usage_history}})[0]->[0]),
scalar localtime($now));
} else {
$self->trace(sprintf "no historical data found");
}
push(@{$self->{usage_history}}, [ time, $self->{percent_used} ]);
$params{save} = $self->{usage_history};
$self->save_state(%params);
}
}
sub nagios {
my $self = shift;
my %params = @_;
if (! $self->{nagios_level}) {
if ($params{mode} =~ /server::database::tablespace::usage/) {
if (! $self->{bytes_max}) {
$self->check_thresholds($self->{percent_used}, "90", "98");
if ($self->{status} eq 'offline') {
$self->add_nagios_warning(
sprintf("tbs %s is offline", $self->{name})
);
} else {
$self->add_nagios_critical(
sprintf("tbs %s has has a problem, maybe needs recovery?", $self->{name})
);
}
} else {
#if ($self->check_thresholds($self->{percent_used}, "90", "98") != '0'){
$self->add_nagios(
# 'tbs_system_usage_pct'=99.01%;90;98 percent used, warn, crit
# 'tbs_system_usage'=693MB;630;686;0;700 used, warn, crit, 0, max=total
$self->check_thresholds($self->{percent_used}, "90", "98"),
$params{eyecandy} ?
sprintf("[%s] %s", $self->{percent_as_bar}, $self->{name}) :
sprintf("tbs %s usage is %.2f%%",
$self->{name}, $self->{percent_used})
);
#}
}
$self->add_perfdata(sprintf "\'%s\'=%dMB",
lc $self->{name},
($self->{bytes} - $self->{bytes_free}) / 1048576);
} elsif ($params{mode} =~ /server::database::tablespace::fragmentation/) {
$self->add_nagios(
$self->check_thresholds($self->{fsfi}, "30:", "20:"),
sprintf "tbs %s fsfi is %.2f", $self->{name}, $self->{fsfi});
$self->add_perfdata(sprintf "\'tbs_%s_fsfi\'=%.2f;%s;%s;0;100",
lc $self->{name},
$self->{fsfi},
$self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~ /server::database::tablespace::free/) {
# ->percent_free
# ->real_bytes_max
#
# ausgabe
# perfdata tbs__free_pct
# perfdata tbs__free (real_bytes_max - bytes) + bytes_free (with units)
# perfdata tbs__alloc_free bytes_free (with units)
#
# umrechnen der thresholds
# ()/%
# MB
# GB
# KB
if (($self->{warningrange} && $self->{warningrange} !~ /^\d+:/) ||
($self->{criticalrange} && $self->{criticalrange} !~ /^\d+:/)) {
$self->add_nagios_unknown("you want an alert if free space is _above_ a threshold????");
return;
}
if (! $params{units}) {
$params{units} = "%";
}
$self->{warning_bytes} = 0;
$self->{critical_bytes} = 0;
if ($params{units} eq "%") {
if (! $self->{bytes_max}) {
$self->check_thresholds($self->{percent_used}, "5:", "2:");
if ($self->{status} eq 'offline') {
$self->add_nagios_warning(
sprintf("tbs %s is offline", $self->{name})
);
} else {
$self->add_nagios_critical(
sprintf("tbs %s has has a problem, maybe needs recovery?", $self->{name})
);
}
} else {
$self->add_nagios(
$self->check_thresholds($self->{percent_free}, "5:", "2:"),
sprintf("tbs %s has %.2f%% free space left",
$self->{name}, $self->{percent_free})
);
}
$self->{warningrange} =~ s/://g;
$self->{criticalrange} =~ s/://g;
$self->add_perfdata(sprintf "\'tbs_%s_free_pct\'=%.2f%%;%d:;%d:",
lc $self->{name},
$self->{percent_free},
$self->{warningrange}, $self->{criticalrange});
$self->add_perfdata(sprintf "\'tbs_%s_free\'=%dMB;%.2f:;%.2f:;0;%.2f",
lc $self->{name},
$self->{real_bytes_free} / 1048576,
$self->{warningrange} * $self->{bytes_max} / 100 / 1048576,
$self->{criticalrange} * $self->{bytes_max} / 100 / 1048576,
$self->{real_bytes_max} / 1048576);
} else {
my $factor = 1024 * 1024; # default MB
if ($params{units} eq "GB") {
$factor = 1024 * 1024 * 1024;
} elsif ($params{units} eq "MB") {
$factor = 1024 * 1024;
} elsif ($params{units} eq "KB") {
$factor = 1024;
}
$self->{warningrange} ||= "5:";
$self->{criticalrange} ||= "2:";
my $saved_warningrange = $self->{warningrange};
my $saved_criticalrange = $self->{criticalrange};
# : entfernen weil gerechnet werden muss
$self->{warningrange} =~ s/://g;
$self->{criticalrange} =~ s/://g;
$self->{warningrange} = $self->{warningrange} ?
$self->{warningrange} * $factor : 5 * $factor;
$self->{criticalrange} = $self->{criticalrange} ?
$self->{criticalrange} * $factor : 2 * $factor;
if (! $self->{bytes_max}) {
$self->{percent_warning} = 0;
$self->{percent_critical} = 0;
$self->{warningrange} .= ':';
$self->{criticalrange} .= ':';
$self->check_thresholds($self->{real_bytes_free}, "5242880:", "1048576:");
if ($self->{status} eq 'offline') {
$self->add_nagios_warning(
sprintf("tbs %s is offline", $self->{name})
);
} else {
$self->add_nagios_critical(
sprintf("tbs %s has a problem, maybe needs recovery?", $self->{name})
);
}
} else {
$self->{percent_warning} = 100 * $self->{warningrange} / $self->{real_bytes_max};
$self->{percent_critical} = 100 * $self->{criticalrange} / $self->{real_bytes_max};
$self->{warningrange} .= ':';
$self->{criticalrange} .= ':';
$self->add_nagios(
$self->check_thresholds($self->{real_bytes_free}, "5242880:", "1048576:"),
sprintf("tbs %s has %.2f%s free space left", $self->{name},
$self->{real_bytes_free} / $factor, $params{units})
);
}
$self->{warningrange} = $saved_warningrange;
$self->{criticalrange} = $saved_criticalrange;
$self->{warningrange} =~ s/://g;
$self->{criticalrange} =~ s/://g;
$self->add_perfdata(sprintf "\'tbs_%s_free_pct\'=%.2f%%;%.2f:;%.2f:",
lc $self->{name},
$self->{percent_free}, $self->{percent_warning},
$self->{percent_critical});
$self->add_perfdata(sprintf "\'tbs_%s_free\'=%.2f%s;%.2f:;%.2f:;0;%.2f",
lc $self->{name},
$self->{real_bytes_free} / $factor, $params{units},
$self->{warningrange},
$self->{criticalrange},
$self->{real_bytes_max} / $factor);
}
} elsif ($params{mode} =~ /server::database::tablespace::fragmentation/) {
$self->add_nagios(
$self->check_thresholds($self->{fsfi}, "30:", "20:"),
sprintf "tbs %s fsfi is %.2f", $self->{name}, $self->{fsfi});
$self->add_perfdata(sprintf "\'tbs_%s_fsfi\'=%.2f;%s;%s;0;100",
lc $self->{name},
$self->{fsfi},
$self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~ /server::database::tablespace::segment::top10/) {
foreach (@{$self->{segments}}) {
$_->nagios(%params);
$self->merge_nagios($_);
}
} elsif ($params{mode} =~ /server::database::tablespace::datafile::listdatafiles/) {
foreach (sort { $a->{name} cmp $b->{name}; } @{$self->{datafiles}}) {
printf "%s\n", $_->{name};
}
$self->add_nagios_ok("have fun");
} elsif ($params{mode} =~ /server::database::tablespace::datafile/) {
foreach (@{$self->{datafiles}}) {
$_->nagios(%params);
$self->merge_nagios($_);
}
} elsif ($params{mode} =~ /server::database::tablespace::iobalance/) {
my $cv = 0;
if (scalar(@{$self->{datafiles}}) == 0) {
$self->add_nagios($self->check_thresholds($cv, 50, 100),
sprintf "%s has no datafiles", $self->{name});
} elsif (scalar(@{$self->{datafiles}}) == 1) {
$self->add_nagios($self->check_thresholds($cv, 50, 100),
sprintf "%s has just 1 datafile", $self->{name});
} elsif ($self->{io_total} == 0) {
# nix los
$self->check_thresholds(0, 50, 100);
$self->add_nagios_ok(sprintf "%s datafiles io is well balanced",
$self->{name});
} else {
my @unbalanced_datafiles = ();
my $worstfactor = 0;
my $level = $ERRORS{OK};
#
#
# arithmetisches mittel der stichprobe "x quer"
my $averagetotal = $self->{io_total} / scalar(@{$self->{datafiles}});
# standardabweichung
my $sum = 0;
foreach (@{$self->{datafiles}}) {
$sum += ($_->{io_total} - $averagetotal) ** 2;
}
my $sx = sqrt ($sum / (scalar(@{$self->{datafiles}}) - 1));
# relative standardabweichung (%RSD)
$cv = $sx / $averagetotal * 100;
# jetzt werden diejenigen datafiles ermittelt, die aus der reihe tanzen
# wie verhaelt sich ihre differenz zum mittelwert zur standardabweichung
foreach my $datafile (@{$self->{datafiles}}) {
my $delta = abs($datafile->{io_total} - $averagetotal);
my $factor = $delta / $sx * 100;
$worstfactor = $factor unless $factor <= $worstfactor;
if ($self->check_thresholds($factor, 50, 100)) {
push(@unbalanced_datafiles, $datafile);
}
}
if ($self->check_thresholds($worstfactor, 50, 100)) {
$self->add_nagios($self->check_thresholds($worstfactor, 50, 100),
sprintf "%s datafiles %s io unbalanced (%f)", $self->{name},
join(",", map { $_->{name} } @unbalanced_datafiles), $worstfactor);
} else {
$self->add_nagios_ok(sprintf "%s datafiles io is well balanced",
$self->{name});
}
}
# coefficient of variation (cv)
$self->add_perfdata(sprintf "\'tbs_%s_io_cv\'=%.2f%%;%.2f;%.2f",
$self->{name}, $cv,
$self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~
/server::database::tablespace::remainingfreetime/) {
my $lookback = $params{lookback} || 30;
my $enoughvalues = 0;
my $newest = time - $lookback * 24 * 3600;
my @tmp = grep { $_->[0] > $newest } @{$self->{usage_history}};
$self->trace(sprintf "found %d usable data sets since %s",
scalar(@tmp), scalar localtime($newest));
if ((scalar(@{$self->{usage_history}}) - scalar(@tmp) > 0) &&
(scalar(@tmp) >= 2)) {
# only if more than two values are available
# only if we have data really reaching back some days
# predicting with two values from the last hour makes no sense
$self->{usage_history} = \@tmp;
my $remaining = 99999;
my $now = time; # normalisieren, so dass jetzt x=0
my $n = 0; my $sumx = 0; my $sumx2 = 0; my $sumxy = 0; my $sumy = 0; my $sumy2 = 0; my $m = 0; my $r = 0;
my $start_usage = undef;
my $stop_usage = undef;
foreach (@{$self->{usage_history}}) {
next if $_->[0] < $newest;
$start_usage = $_->[1] if ! defined $start_usage;
$stop_usage = $_->[1];
my $x = ($_->[0] - $now) / (24 * 3600);
my $y = $_->[1];
$n++; # increment number of data points by 1
$sumx += $x; # compute sum of x
$sumx2 += $x * $x; # compute sum of x**2
$sumxy += $x * $y; # compute sum of x * y
$sumy += $y; # compute sum of y
$sumy2 += $y * $y; # compute sum of y**2
}
# compute slope
$m = ($n * $sumxy - $sumx * $sumy) / ($n * $sumx2 - $sumx ** 2);
# compute y-intercept
$b = ($sumy * $sumx2 - $sumx * $sumxy) / ($n * $sumx2 - $sumx ** 2);
# compute correlation coefficient
#$r = ($sumxy - $sumx * $sumy / $n) /
# sqrt(($sumx2 - ($sumx ** 2)/$n) * ($sumy2 - ($sumy ** 2)/$n));
$self->debug(sprintf "slope: %f y-intersect: %f", $m, $b);
if (abs($m) <= 0.000001) { # $m == 0 does not work even if $m is 0.000000
$self->add_nagios_ok("tablespace usage is constant");
} elsif ($m > 0) {
$remaining = (100 - $b) / $m;
$self->add_nagios($self->check_thresholds($remaining, "90:", "30:"),
sprintf "tablespace %s will be full in %d days",
$self->{name}, $remaining);
$self->add_perfdata(sprintf "\'tbs_%s_days_until_full\'=%d;%s;%s",
lc $self->{name},
$remaining,
$self->{warningrange}, $self->{criticalrange});
} else {
$self->add_nagios_ok("tablespace usage is decreasing");
}
} else {
$self->add_nagios_ok("no data available for prediction");
}
} elsif ($params{mode} =~
/server::database::tablespace::segment::extendspace/) {
my $segments = 0;
my @largesegments = ();
foreach my $segment (@{$self->{segments}}) {
$segments++;
$segment->nagios(%params);
if ($segment->{nagios_level}) {
push(@largesegments, $segment->{name});
}
#$self->merge_nagios($segment);
}
if (! $segments) {
$self->add_nagios_ok(
sprintf "tablespace %s has no segments", $self->{name});
} elsif (@largesegments) {
if ($self->{allocation_type} ne "SYSTEM") {
$self->add_nagios_critical(
sprintf "tablespace %s cannot extend segment(s) %s", $self->{name},
join(", ", @largesegments));
} else {
$self->add_nagios_ok(
sprintf "tablespace %s free extents are large enough (autoallocate)",
$self->{name});
}
} elsif (! $self->{nagios_level}) {
$self->add_nagios_ok(
sprintf "tablespace %s free extents are large enough",
$self->{name});
}
} elsif ($params{mode} =~ /server::database::tablespace::datafile/) {
printf "%s\n", $self->dump();
}
}
}
package DBD::Oracle::Server::Database;
use strict;
our @ISA = qw(DBD::Oracle::Server);
sub new {
my $class = shift;
my %params = @_;
my $self = {
handle => $params{handle},
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
invalidobjects => {
invalid_objects => undef,
invalid_indexes => undef,
invalid_ind_partitions => undef,
},
staleobjects => undef,
tablespaces => [],
};
bless $self, $class;
$self->init(%params);
return $self;
}
sub init {
my $self = shift;
my %params = @_;
$self->init_nagios();
if ($params{mode} =~ /server::database::tablespace/) {
DBD::Oracle::Server::Database::Tablespace::init_tablespaces(%params);
if (my @tablespaces =
DBD::Oracle::Server::Database::Tablespace::return_tablespaces()) {
$self->{tablespaces} = \@tablespaces;
} else {
$self->add_nagios_critical("unable to aquire tablespace info");
}
} elsif ($params{mode} =~ /server::database::invalidobjects/) {
$self->init_invalid_objects(%params);
} elsif ($params{mode} =~ /server::database::stalestats/) {
$self->init_stale_objects(%params);
}
}
sub init_invalid_objects {
my $self = shift;
my %params = @_;
my $invalid_objects = undef;
my $invalid_indexes = undef;
my $invalid_ind_partitions = undef;
my $unrecoverable_datafiles = undef;
$self->{invalidobjects}->{invalid_objects} =
$self->{handle}->fetchrow_array(q{
SELECT COUNT(*)
FROM dba_objects
WHERE status = 'INVALID' AND object_name NOT LIKE 'BIN$%'
AND object_type NOT IN ('MATERIALIZED VIEW')
});
# should be only N/A or VALID
$self->{invalidobjects}->{invalid_indexes} =
$self->{handle}->fetchrow_array(q{
SELECT COUNT(DISTINCT OWNER||'.'||INDEX_NAME)
FROM dba_indexes
WHERE status <> 'VALID' AND status <> 'N/A'
});
# should be only USABLE or N/A
$self->{invalidobjects}->{invalid_ind_partitions} =
$self->{handle}->fetchrow_array(q{
SELECT COUNT(DISTINCT INDEX_OWNER||'.'||INDEX_NAME)
FROM dba_ind_partitions
WHERE status <> 'USABLE' AND status <> 'N/A'
});
if (! defined $self->{invalidobjects}->{invalid_objects} ||
! defined $self->{invalidobjects}->{invalid_indexes} ||
! defined $self->{invalidobjects}->{invalid_ind_partitions}) {
$self->add_nagios_critical("unable to get invalid objects");
return undef;
}
}
sub init_stale_objects {
my $self = shift;
my %params = @_;
if ($self->version_is_minimum("10.x")) {
$self->{staleobjects} = $self->{handle}->fetchrow_array(q{
SELECT COUNT(*) FROM sys.dba_tab_statistics WHERE stale_stats = 'YES' AND table_name not like 'BIN$%'
});
} else {
# oracle9 + sqlplus nix gut
$self->{handle}->func( 10000, 'dbms_output_enable' );
$self->{handle}->execute(q{
DECLARE
l_objList dbms_stats.objectTab;
BEGIN
DBMS_OUTPUT.ENABLE (1000000);
dbms_stats.gather_database_stats(
options => 'LIST STALE',
objlist => l_objList );
dbms_output.put_line( l_objList.COUNT);
-- FOR i IN 1 .. l_objList.COUNT
-- LOOP
-- dbms_output.put_line( l_objList(i).objType );
-- dbms_output.put_line( l_objList(i).objName );
-- END LOOP;
END;
});
$self->{staleobjects} = $self->{handle}->func( 'dbms_output_get' );
}
if (! defined $self->{staleobjects}) {
$self->add_nagios_critical("unable to get stale objects");
return undef;
}
}
sub nagios {
my $self = shift;
my %params = @_;
if (! $self->{nagios_level}) {
if ($params{mode} =~ /server::database::tablespace::listtablespaces/) {
foreach (sort { $a->{name} cmp $b->{name}; } @{$self->{tablespaces}}) {
printf "%s\n", $_->{name};
}
$self->add_nagios_ok("have fun");
} elsif ($params{mode} =~ /server::database::tablespace/) {
foreach (@{$self->{tablespaces}}) {
# sind hier noch nach pctused sortiert
$_->nagios(%params);
$self->merge_nagios($_);
}
} elsif ($params{mode} =~ /server::database::invalidobjects/) {
my @message = ();
push(@message, sprintf "%d invalid objects",
$self->{invalidobjects}->{invalid_objects}) if
$self->{invalidobjects}->{invalid_objects};
push(@message, sprintf "%d invalid indexes",
$self->{invalidobjects}->{invalid_indexes}) if
$self->{invalidobjects}->{invalid_indexes};
push(@message, sprintf "%d invalid index partitions",
$self->{invalidobjects}->{invalid_ind_partitions}) if
$self->{invalidobjects}->{invalid_ind_partitions};
if (scalar(@message)) {
$self->add_nagios($self->check_thresholds(
$self->{invalidobjects}->{invalid_objects} +
$self->{invalidobjects}->{invalid_indexes} +
$self->{invalidobjects}->{invalid_ind_partitions}, 0.1, 0.1),
join(", ", @message));
} else {
$self->add_nagios_ok("no invalid objects found");
}
foreach (sort keys %{$self->{invalidobjects}}) {
$self->add_perfdata(sprintf "%s=%d", $_, $self->{invalidobjects}->{$_});
}
} elsif ($params{mode} =~ /server::database::stalestats/) {
$self->add_nagios(
$self->check_thresholds($self->{staleobjects}, "10", "100"),
sprintf "%d objects with stale statistics", $self->{staleobjects});
$self->add_perfdata(sprintf "stale_stats_objects=%d;%s;%s",
$self->{staleobjects},
$self->{warningrange}, $self->{criticalrange});
}
}
}
package DBD::Oracle::Server;
use strict;
use Time::HiRes;
use IO::File;
use File::Copy 'cp';
use Data::Dumper;
{
our $verbose = 0;
our $scream = 0; # scream if something is not implemented
our $access = "dbi"; # how do we access the database.
our $my_modules_dyn_dir = ""; # where we look for self-written extensions
my @servers = ();
my $initerrors = undef;
sub add_server {
push(@servers, shift);
}
sub return_servers {
return @servers;
}
sub return_first_server() {
return $servers[0];
}
}
sub new {
my $class = shift;
my %params = @_;
my $self = {
access => $params{method} || "dbi",
connect => $params{connect},
username => $params{username},
password => $params{password},
timeout => $params{timeout},
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
version => 'unknown',
instance => undef,
database => undef,
handle => undef,
};
bless $self, $class;
$self->init_nagios();
if ($self->dbconnect(%params)) {
$self->{version} = $self->{handle}->fetchrow_array(
q{ SELECT version FROM v$instance });
$self->{os} = $self->{handle}->fetchrow_array(
q{ SELECT dbms_utility.port_string FROM dual });
$self->{dbuser} = $self->{handle}->fetchrow_array(
q{ SELECT sys_context('userenv', 'session_user') FROM dual });
$self->{thread} = $self->{handle}->fetchrow_array(
q{ SELECT thread# FROM v$instance });
$self->{parallel} = $self->{handle}->fetchrow_array(
q{ SELECT parallel FROM v$instance });
DBD::Oracle::Server::add_server($self);
$self->init(%params);
}
return $self;
}
sub init {
my $self = shift;
my %params = @_;
$params{handle} = $self->{handle};
if ($params{mode} =~ /^server::instance/) {
$self->{instance} = DBD::Oracle::Server::Instance->new(%params);
} elsif ($params{mode} =~ /^server::database/) {
$self->{database} = DBD::Oracle::Server::Database->new(%params);
} elsif ($params{mode} =~ /^server::sql/) {
@{$self->{genericsql}} =
$self->{handle}->fetchrow_array($params{selectname});
if (! (defined $self->{genericsql} &&
(scalar(grep { /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/ } @{$self->{genericsql}})) ==
scalar(@{$self->{genericsql}}))) {
$self->add_nagios_unknown(sprintf "got no valid response for %s",
$params{selectname});
} else {
# name2 in array
# units in array
}
} elsif ($params{mode} =~ /^server::connectiontime/) {
$self->{connection_time} = $self->{tac} - $self->{tic};
} elsif ($params{mode} =~ /^my::([^:.]+)/) {
my $class = $1;
my $loaderror = undef;
substr($class, 0, 1) = uc substr($class, 0, 1);
foreach my $libpath (split(":", $DBD::Oracle::Server::my_modules_dyn_dir)) {
foreach my $extmod (glob $libpath."/CheckOracleHealth*.pm") {
eval {
$self->trace(sprintf "loading module %s", $extmod);
require $extmod;
};
if ($@) {
$loaderror = $extmod;
$self->trace(sprintf "failed loading module %s: %s", $extmod, $@);
}
}
}
my $obj = {
handle => $params{handle},
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
};
bless $obj, "My$class";
$self->{my} = $obj;
if ($self->{my}->isa("DBD::Oracle::Server")) {
my $dos_init = $self->can("init");
my $dos_nagios = $self->can("nagios");
my $my_init = $self->{my}->can("init");
my $my_nagios = $self->{my}->can("nagios");
if ($my_init == $dos_init) {
$self->add_nagios_unknown(
sprintf "Class %s needs an init() method", ref($self->{my}));
} elsif ($my_nagios == $dos_nagios) {
$self->add_nagios_unknown(
sprintf "Class %s needs a nagios() method", ref($self->{my}));
} else {
$self->{my}->init_nagios(%params);
$self->{my}->init(%params);
}
} else {
$self->add_nagios_unknown(
sprintf "Class %s is not a subclass of DBD::Oracle::Server%s",
ref($self->{my}),
$loaderror ? sprintf " (syntax error in %s?)", $loaderror : "" );
}
} else {
printf "broken mode %s\n", $params{mode};
}
}
sub dump {
my $self = shift;
my $message = shift || "";
printf "%s %s\n", $message, Data::Dumper::Dumper($self);
}
sub nagios {
my $self = shift;
my %params = @_;
if (! $self->{nagios_level}) {
if ($params{mode} =~ /^server::instance/) {
$self->{instance}->nagios(%params);
$self->merge_nagios($self->{instance});
} elsif ($params{mode} =~ /^server::database/) {
$self->{database}->nagios(%params);
$self->merge_nagios($self->{database});
} elsif ($params{mode} =~ /^server::connectiontime/) {
$self->add_nagios(
$self->check_thresholds($self->{connection_time}, 1, 5),
sprintf "%.2f seconds to connect as %s",
$self->{connection_time}, $self->{dbuser});
$self->add_perfdata(sprintf "connection_time=%.4f;%d;%d",
$self->{connection_time},
$self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~ /^server::sql/) {
$self->add_nagios(
# the first item in the list will trigger the threshold values
$self->check_thresholds($self->{genericsql}[0], 1, 5),
sprintf "%s: %s%s",
$params{name2} ? lc $params{name2} : lc $params{selectname},
# float as float, integers as integers
join(" ", map {
(sprintf("%d", $_) eq $_) ? $_ : sprintf("%f", $_)
} @{$self->{genericsql}}),
$params{units} ? $params{units} : "");
my $i = 0;
# workaround... getting the column names from the database would be nicer
my @names2_arr = split(/\s+/, $params{name2});
foreach my $t (@{$self->{genericsql}}) {
$self->add_perfdata(sprintf "\'%s\'=%s%s;%s;%s",
$names2_arr[$i] ? lc $names2_arr[$i] : lc $params{selectname},
# float as float, integers as integers
(sprintf("%d", $t) eq $t) ? $t : sprintf("%f", $t),
$params{units} ? $params{units} : "",
($i == 0) ? $self->{warningrange} : "",
($i == 0) ? $self->{criticalrange} : ""
);
$i++;
}
} elsif ($params{mode} =~ /^my::([^:.]+)/) {
$self->{my}->nagios(%params);
$self->merge_nagios($self->{my});
}
}
}
sub init_nagios {
my $self = shift;
no strict 'refs';
if (! ref($self)) {
my $nagiosvar = $self."::nagios";
my $nagioslevelvar = $self."::nagios_level";
$$nagiosvar = {
messages => {
0 => [],
1 => [],
2 => [],
3 => [],
},
perfdata => [],
};
$$nagioslevelvar = $ERRORS{OK},
} else {
$self->{nagios} = {
messages => {
0 => [],
1 => [],
2 => [],
3 => [],
},
perfdata => [],
};
$self->{nagios_level} = $ERRORS{OK},
}
}
sub check_thresholds {
my $self = shift;
my $value = shift;
my $defaultwarningrange = shift;
my $defaultcriticalrange = shift;
my $level = $ERRORS{OK};
$self->{warningrange} = defined $self->{warningrange} ?
$self->{warningrange} : $defaultwarningrange;
$self->{criticalrange} = defined $self->{criticalrange} ?
$self->{criticalrange} : $defaultcriticalrange;
if ($self->{warningrange} !~ /:/ && $self->{criticalrange} !~ /:/) {
# warning = 10, critical = 20, warn if > 10, crit if > 20
$level = $ERRORS{WARNING} if $value > $self->{warningrange};
$level = $ERRORS{CRITICAL} if $value > $self->{criticalrange};
} elsif ($self->{warningrange} =~ /(\d+):/ &&
$self->{criticalrange} =~ /(\d+):/) {
# warning = 98:, critical = 95:, warn if < 98, crit if < 95
$self->{warningrange} =~ /(\d+):/;
$level = $ERRORS{WARNING} if $value < $1;
$self->{criticalrange} =~ /(\d+):/;
$level = $ERRORS{CRITICAL} if $value < $1;
}
return $level;
#
# syntax error must be reported with returncode -1
#
}
sub add_nagios {
my $self = shift;
my $level = shift;
my $message = shift;
push(@{$self->{nagios}->{messages}->{$level}}, $message);
# recalc current level
foreach my $llevel qw(CRITICAL WARNING UNKNOWN OK) {
if (scalar(@{$self->{nagios}->{messages}->{$ERRORS{$llevel}}})) {
$self->{nagios_level} = $ERRORS{$llevel};
}
}
}
sub add_nagios_ok {
my $self = shift;
my $message = shift;
$self->add_nagios($ERRORS{OK}, $message);
}
sub add_nagios_warning {
my $self = shift;
my $message = shift;
$self->add_nagios($ERRORS{WARNING}, $message);
}
sub add_nagios_critical {
my $self = shift;
my $message = shift;
$self->add_nagios($ERRORS{CRITICAL}, $message);
}
sub add_nagios_unknown {
my $self = shift;
my $message = shift;
$self->add_nagios($ERRORS{UNKNOWN}, $message);
}
sub add_perfdata {
my $self = shift;
my $data = shift;
push(@{$self->{nagios}->{perfdata}}, $data);
}
sub merge_nagios {
my $self = shift;
my $child = shift;
foreach my $level (0..3) {
foreach (@{$child->{nagios}->{messages}->{$level}}) {
$self->add_nagios($level, $_);
}
#push(@{$self->{nagios}->{messages}->{$level}},
# @{$child->{nagios}->{messages}->{$level}});
}
push(@{$self->{nagios}->{perfdata}}, @{$child->{nagios}->{perfdata}});
}
sub calculate_result {
my $self = shift;
if ($ENV{NRPE_MULTILINESUPPORT} &&
length join(" ", @{$self->{nagios}->{perfdata}}) > 200) {
foreach my $level ("CRITICAL", "WARNING", "UNKNOWN", "OK") {
# first the bad news
if (scalar(@{$self->{nagios}->{messages}->{$ERRORS{$level}}})) {
$self->{nagios_message} .=
"\n".join("\n", @{$self->{nagios}->{messages}->{$ERRORS{$level}}});
}
}
$self->{nagios_message} =~ s/^\n//g;
$self->{perfdata} = join("\n", @{$self->{nagios}->{perfdata}});
} else {
foreach my $level ("CRITICAL", "WARNING", "UNKNOWN", "OK") {
# first the bad news
if (scalar(@{$self->{nagios}->{messages}->{$ERRORS{$level}}})) {
$self->{nagios_message} .=
join(", ", @{$self->{nagios}->{messages}->{$ERRORS{$level}}}).", ";
}
}
$self->{nagios_message} =~ s/, $//g;
$self->{perfdata} = join(" ", @{$self->{nagios}->{perfdata}});
}
foreach my $level ("OK", "UNKNOWN", "WARNING", "CRITICAL") {
if (scalar(@{$self->{nagios}->{messages}->{$ERRORS{$level}}})) {
$self->{nagios_level} = $ERRORS{$level};
}
}
}
sub debug {
my $self = shift;
my $msg = shift;
if ($DBD::Oracle::Server::verbose) {
printf "%s %s\n", $msg, ref($self);
}
}
sub dbconnect {
my $self = shift;
my %params = @_;
my $retval = undef;
$self->{tic} = Time::HiRes::time();
$self->{handle} = DBD::Oracle::Server::Connection->new(%params);
if ($self->{handle}->{errstr}) {
if ($params{mode} =~ /^server::tnsping/ &&
$self->{handle}->{errstr} =~ /ORA-01017/) {
$self->add_nagios($ERRORS{OK},
sprintf "connection established to %s.", $self->{connect});
$retval = undef;
} elsif ($self->{handle}->{errstr} eq "alarm\n") {
$self->add_nagios($ERRORS{CRITICAL},
sprintf "connection could not be established within %d seconds",
$self->{timeout});
} else {
if ($self->{connect} =~ /^(.*?)\/(.*)@(.*)$/) {
$self->{connect} = sprintf "%s/***@%s", $1, $3;
} elsif ($self->{connect} =~ /^(.*?)@(.*)$/) {
$self->{connect} = sprintf "%s@%s", $1, $2;
}
$self->add_nagios($ERRORS{CRITICAL},
sprintf "cannot connect to %s. %s",
$self->{connect}, $self->{handle}->{errstr});
$retval = undef;
}
} else {
$retval = $self->{handle};
}
$self->{tac} = Time::HiRes::time();
return $retval;
}
sub trace {
my $self = shift;
my $format = shift;
$self->{trace} = -f "/tmp/check_oracle_health.trace" ? 1 : 0;
if ($self->{verbose}) {
printf("%s: ", scalar localtime);
printf($format, @_);
}
if ($self->{trace}) {
my $logfh = new IO::File;
$logfh->autoflush(1);
if ($logfh->open("/tmp/check_oracle_health.trace", "a")) {
$logfh->printf("%s: ", scalar localtime);
$logfh->printf($format, @_);
$logfh->printf("\n");
$logfh->close();
}
}
}
sub DESTROY {
my $self = shift;
my $handle1 = "null";
my $handle2 = "null";
if (defined $self->{handle}) {
$handle1 = ref($self->{handle});
if (defined $self->{handle}->{handle}) {
$handle2 = ref($self->{handle}->{handle});
}
}
#$self->trace(sprintf "DESTROY %s with handle %s %s", ref($self), $handle1, $handle2);
if (ref($self) eq "DBD::Oracle::Server") {
}
#$self->trace(sprintf "DESTROY %s exit with handle %s %s", ref($self), $handle1, $handle2);
if (ref($self) eq "DBD::Oracle::Server") {
#printf "humpftata\n";
}
}
sub save_state {
my $self = shift;
my %params = @_;
my $extension = "";
if ($params{connect} =~ /(\w+)\/(\w+)@(\w+)/) {
$params{connect} = $3;
} else {
# just to be sure
$params{connect} =~ s/\//_/g;
}
mkdir $params{statefilesdir} unless -d $params{statefilesdir};
my $statefile = sprintf "%s/%s_%s",
$params{statefilesdir}, $params{connect}, $params{mode};
$extension .= $params{differenciator} ? "_".$params{differenciator} : "";
$extension .= $params{tablespace} ? "_".$params{tablespace} : "";
$extension .= $params{datafile} ? "_".$params{datafile} : "";
$extension .= $params{name} ? "_".$params{name} : "";
$extension =~ s/\//_/g;
$extension =~ s/\(/_/g;
$extension =~ s/\)/_/g;
$extension =~ s/\*/_/g;
$extension =~ s/\s/_/g;
$statefile .= $extension;
$statefile = lc $statefile;
open(STATE, ">$statefile");
if ((ref($params{save}) eq "HASH") && exists $params{save}->{timestamp}) {
$params{save}->{localtime} = scalar localtime $params{save}->{timestamp};
}
printf STATE Data::Dumper::Dumper($params{save});
close STATE;
$self->debug(sprintf "saved %s to %s",
Data::Dumper::Dumper($params{save}), $statefile);
}
sub load_state {
my $self = shift;
my %params = @_;
my $extension = "";
if ($params{connect} =~ /(\w+)\/(\w+)@(\w+)/) {
$params{connect} = $3;
} else {
$params{connect} =~ s/\//_/g;
}
my $statefile = sprintf "%s/%s_%s",
$params{statefilesdir}, $params{connect}, $params{mode};
$extension .= $params{differenciator} ? "_".$params{differenciator} : "";
$extension .= $params{tablespace} ? "_".$params{tablespace} : "";
$extension .= $params{datafile} ? "_".$params{datafile} : "";
$extension .= $params{name} ? "_".$params{name} : "";
$extension =~ s/\//_/g;
$extension =~ s/\(/_/g;
$extension =~ s/\)/_/g;
$extension =~ s/\*/_/g;
$extension =~ s/\s/_/g;
$statefile .= $extension;
$statefile = lc $statefile;
if ( -f $statefile) {
our $VAR1;
eval {
require $statefile;
};
if($@) {
printf "rumms\n";
}
$self->debug(sprintf "load %s", Data::Dumper::Dumper($VAR1));
return $VAR1;
} else {
return undef;
}
}
sub valdiff {
my $self = shift;
my $pparams = shift;
my %params = %{$pparams};
my @keys = @_;
my $last_values = $self->load_state(%params) || eval {
my $empty_events = {};
foreach (@keys) {
$empty_events->{$_} = 0;
}
$empty_events->{timestamp} = 0;
$empty_events;
};
foreach (@keys) {
$last_values->{$_} = 0 if ! exists $last_values->{$_};
if ($self->{$_} >= $last_values->{$_}) {
$self->{'delta_'.$_} = $self->{$_} - $last_values->{$_};
} else {
# vermutlich db restart und zaehler alle auf null
$self->{'delta_'.$_} = $self->{$_};
}
$self->debug(sprintf "delta_%s %f", $_, $self->{'delta_'.$_});
}
$self->{'delta_timestamp'} = time - $last_values->{timestamp};
$params{save} = eval {
my $empty_events = {};
foreach (@keys) {
$empty_events->{$_} = $self->{$_};
}
$empty_events->{timestamp} = time;
$empty_events;
};
$self->save_state(%params);
}
sub requires_version {
my $self = shift;
my $version = shift;
my @instances = DBD::Oracle::Server::return_servers();
my $instversion = $instances[0]->{version};
if (! $self->version_is_minimum($version)) {
$self->add_nagios($ERRORS{UNKNOWN},
sprintf "not implemented/possible for Oracle release %s", $instversion);
}
}
sub version_is_minimum {
# the current version is newer or equal
my $self = shift;
my $version = shift;
my $newer = 1;
my @instances = DBD::Oracle::Server::return_servers();
my @v1 = map { $_ eq "x" ? 0 : $_ } split(/\./, $version);
my @v2 = split(/\./, $instances[0]->{version});
if (scalar(@v1) > scalar(@v2)) {
push(@v2, (0) x (scalar(@v1) - scalar(@v2)));
} elsif (scalar(@v2) > scalar(@v1)) {
push(@v1, (0) x (scalar(@v2) - scalar(@v1)));
}
foreach my $pos (0..$#v1) {
if ($v2[$pos] > $v1[$pos]) {
$newer = 1;
last;
} elsif ($v2[$pos] < $v1[$pos]) {
$newer = 0;
last;
}
}
#printf STDERR "check if %s os minimum %s\n", join(".", @v2), join(".", @v1);
return $newer;
}
sub instance_rac {
my $self = shift;
my @instances = DBD::Oracle::Server::return_servers();
return (lc $instances[0]->{parallel} eq "yes") ? 1 : 0;
}
sub instance_thread {
my $self = shift;
my @instances = DBD::Oracle::Server::return_servers();
return $instances[0]->{thread};
}
sub windows_server {
my $self = shift;
my @instances = DBD::Oracle::Server::return_servers();
if ($instances[0]->{os} =~ /Win/i) {
return 1;
} else {
return 0;
}
}
sub system_vartmpdir {
my $self = shift;
if ($^O =~ /MSWin/) {
return $self->system_tmpdir();
} else {
return "/var/tmp/check_oracle_health";
}
}
sub system_oldvartmpdir {
my $self = shift;
return "/tmp";
}
sub system_tmpdir {
my $self = shift;
if ($^O =~ /MSWin/) {
return $ENV{TEMP} if defined $ENV{TEMP};
return $ENV{TMP} if defined $ENV{TMP};
return File::Spec->catfile($ENV{windir}, 'Temp')
if defined $ENV{windir};
return 'C:\Temp';
} else {
return "/tmp";
}
}
package DBD::Oracle::Server::Connection;
use strict;
our @ISA = qw(DBD::Oracle::Server);
sub new {
my $class = shift;
my %params = @_;
my $self = {
mode => $params{mode},
timeout => $params{timeout},
access => $params{method} || "dbi",
connect => $params{connect},
username => $params{username},
password => $params{password},
tnsadmin => $ENV{TNS_ADMIN},
oraclehome => $ENV{ORACLE_HOME},
handle => undef,
};
bless $self, $class;
if ($params{method} eq "dbi") {
bless $self, "DBD::Oracle::Server::Connection::Dbi";
} elsif ($params{method} eq "sqlplus") {
bless $self, "DBD::Oracle::Server::Connection::Sqlplus";
} elsif ($params{method} eq "sqlrelay") {
bless $self, "DBD::Oracle::Server::Connection::Sqlrelay";
}
$self->init(%params);
return $self;
}
package DBD::Oracle::Server::Connection::Dbi;
use strict;
use Net::Ping;
our @ISA = qw(DBD::Oracle::Server::Connection);
sub init {
my $self = shift;
my %params = @_;
my $retval = undef;
if ($self->{mode} =~ /^server::tnsping/) {
if (! $self->{connect}) {
$self->{errstr} = "Please specify a database";
} else {
$self->{sid} = $self->{connect};
$self->{username} ||= time; # prefer an existing user
$self->{password} ||= time;
}
} else {
if (! $self->{connect} || ! $self->{username} || ! $self->{password}) {
if ($self->{connect} && $self->{connect} =~ /(\w+)\/(\w+)@(\w+)/) {
$self->{connect} = $3;
$self->{username} = $1;
$self->{password} = $2;
$self->{sid} = $self->{connect};
} elsif ($self->{connect} && $self->{connect} =~ /^(sys|sysdba)@(\w+)/) {
$self->{connect} = $2;
$self->{username} = $1;
$self->{password} = '';
$self->{sid} = $self->{connect};
} elsif ($self->{connect} && ! $self->{username} && ! $self->{password}) {
# maybe this is a os authenticated user
delete $ENV{TWO_TASK};
$self->{sid} = $self->{connect};
if ($^O ne "hpux") { #hpux && 1.21 only accepts "DBI:Oracle:SID"
$self->{connect} = ""; #linux 1.20 only accepts "DBI:Oracle:" + ORACLE_SID
}
$self->{username} = '/';
$self->{password} = "";
} else {
$self->{errstr} = "Please specify database, username and password";
return undef;
}
} else {
$self->{sid} = $self->{connect};
}
}
if (! exists $self->{errstr}) {
$ENV{ORACLE_SID} = $self->{sid};
eval {
require DBI;
use POSIX ':signal_h';
local $SIG{'ALRM'} = sub {
die "alarm\n";
};
my $mask = POSIX::SigSet->new( SIGALRM );
my $action = POSIX::SigAction->new(
sub { die "alarm\n" ; }, $mask);
my $oldaction = POSIX::SigAction->new();
sigaction(SIGALRM ,$action ,$oldaction );
alarm($self->{timeout} - 1); # 1 second before the global unknown timeout
my $dsn = sprintf "DBI:Oracle:%s", $self->{connect};
my $connecthash = { RaiseError => 0, AutoCommit => 0, PrintError => 0 };
if ($self->{username} eq "sys" || $self->{username} eq "sysdba") {
$connecthash = { RaiseError => 0, AutoCommit => 0, PrintError => 0,
#ora_session_mode => DBD::Oracle::ORA_SYSDBA
ora_session_mode => 0x0002 };
$dsn = sprintf "DBI:Oracle:";
}
if ($self->{handle} = DBI->connect(
$dsn,
$self->{username},
$self->{password},
$connecthash)) {
$self->{handle}->do(q{
ALTER SESSION SET NLS_NUMERIC_CHARACTERS=".," });
$retval = $self;
if ($self->{mode} =~ /^server::tnsping/) {
$self->{handle}->disconnect();
die "ORA-01017"; # fake a successful connect with wrong password
}
} else {
$self->{errstr} = DBI::errstr();
}
};
if ($@) {
$self->{errstr} = $@;
$retval = undef;
}
}
$self->{tac} = Time::HiRes::time();
return $retval;
}
sub tnsping {
my $self = shift;
my $retval = undef;
if ($self->{tnsadmin}) {
$self->{tnsfile} = $self->{tnsadmin}.'/tnanames.ora';
} elsif ($self->{oraclehome}) {
$self->{tnsfile} = $self->{oraclehome}.'/network/admin/tnsnames.ora';
} else {
$self->{tnsfile} = $ENV{HOME}.'/tnsnames.ora';
}
my $re_blank = '^$';
my $re_comment = '^#';
my $re_tns_sentry = '^'.$self->{sid}.'.*?='; # specific entry
my $re_tns_gentry = '^\w.*?='; # generic entry
my $re_tns_pair = '\w+\s*\=\s*[\w\.]+'; # used to parse key=val
my $re_keys = 'host|port|sid';
my @tnsfile = split(/\n/, do { local (@ARGV, $/) = $self->{tnsfile}; <> });
my $found = 0;
my $datastring = "";
foreach (@tnsfile) {
chomp;
next if /$re_blank/;
next if /$re_comment/;
if (/$re_tns_sentry/) {
$found = 1;
s/$re_tns_sentry//;
$datastring = $_;
}
if ($found) {
last if /$re_tns_gentry/;
$datastring .= $_;
}
}
if ($found) {
while ($datastring =~ m/($re_tns_pair)/g) {
my ($key, $value) = split(/=/, $1);
$key =~ s/^\s+//g;
$value =~ s/^\s+//g;
$key =~ s/\s+$//g;
$value =~ s/\s+$//g;
next unless $key =~ /$re_keys/i;
if (lc $key eq "host") {
$self->{hostname} = $value;
} elsif (lc $key eq "port") {
$self->{port} = $value;
}
}
}
if (exists $self->{hostname} && exists $self->{port}) {
my $p = Net::Ping->new("tcp");
$p->{port_num} = $self->{port};
if ($p->ping($self->{hostname}, $self->{timeout} - 1)) {
$self->{handle} = 1;
$retval = $self;
} else {
$self->{errstr} = sprintf "tnsping timed out after %d seconds",
$self->{timeout};
}
} else {
$self->{errstr} = sprintf "could not find host and address for %s",
$self->{sid};
}
return $retval;
}
sub fetchrow_array {
my $self = shift;
my $sql = shift;
my @arguments = @_;
my $sth = undef;
my @row = ();
$self->trace(sprintf "fetchrow_array: %s", $sql);
$self->trace(sprintf "args: %s", Data::Dumper::Dumper(\@arguments));
eval {
$sth = $self->{handle}->prepare($sql);
if (scalar(@arguments)) {
$sth->execute(@arguments);
} else {
$sth->execute();
}
@row = $sth->fetchrow_array();
};
if ($@) {
$self->debug(sprintf "bumm %s", $@);
}
if (-f "/tmp/check_oracle_health_simulation/".$self->{mode}) {
my $simulation = do { local (@ARGV, $/) =
"/tmp/check_oracle_health_simulation/".$self->{mode}; <> };
@row = split(/\s+/, (split(/\n/, $simulation))[0]);
}
return $row[0] unless wantarray;
return @row;
}
sub fetchall_array {
my $self = shift;
my $sql = shift;
my @arguments = @_;
my $sth = undef;
my $rows = undef;
$self->trace(sprintf "fetchall_array: %s", $sql);
$self->trace(sprintf "args: %s", Data::Dumper::Dumper(\@arguments));
eval {
$sth = $self->{handle}->prepare($sql);
if (scalar(@arguments)) {
$sth->execute(@arguments);
} else {
$sth->execute();
}
$rows = $sth->fetchall_arrayref();
};
if ($@) {
printf STDERR "bumm %s\n", $@;
}
if (-f "/tmp/check_oracle_health_simulation/".$self->{mode}) {
my $simulation = do { local (@ARGV, $/) =
"/tmp/check_oracle_health_simulation/".$self->{mode}; <> };
@{$rows} = map { [ split(/\s+/, $_) ] } split(/\n/, $simulation);
}
return @{$rows};
}
sub func {
my $self = shift;
$self->{handle}->func(@_);
}
sub execute {
my $self = shift;
my $sql = shift;
eval {
my $sth = $self->{handle}->prepare($sql);
$sth->execute();
};
if ($@) {
printf STDERR "bumm %s\n", $@;
}
}
sub DESTROY {
my $self = shift;
$self->trace(sprintf "disconnecting DBD %s",
$self->{handle} ? "with handle" : "without handle");
$self->{handle}->disconnect() if $self->{handle};
}
package DBD::Oracle::Server::Connection::Sqlplus;
use strict;
use File::Temp qw/tempfile/;
our @ISA = qw(DBD::Oracle::Server::Connection);
sub init {
my $self = shift;
my %params = @_;
my $retval = undef;
$self->{loginstring} = "traditional";
($self->{sql_commandfile_handle}, $self->{sql_commandfile}) =
tempfile($self->{mode}."XXXXX", SUFFIX => ".sql",
DIR => $self->system_tmpdir() );
close $self->{sql_commandfile_handle};
($self->{sql_resultfile_handle}, $self->{sql_resultfile}) =
tempfile($self->{mode}."XXXXX", SUFFIX => ".out",
DIR => $self->system_tmpdir() );
close $self->{sql_resultfile_handle};
if ($self->{mode} =~ /^server::tnsping/) {
if (! $self->{connect}) {
$self->{errstr} = "Please specify a database";
} else {
$self->{sid} = $self->{connect};
$self->{username} ||= time; # prefer an existing user
$self->{password} = time;
}
} else {
if ($self->{connect} && ! $self->{username} && ! $self->{password} &&
$self->{connect} =~ /(\w+)\/(\w+)@(\w+)/) {
# --connect nagios/oradbmon@bba
$self->{connect} = $3;
$self->{username} = $1;
$self->{password} = $2;
$self->{sid} = $self->{connect};
if ($self->{username} eq "sys") {
delete $ENV{TWO_TASK};
$self->{loginstring} = "sys";
} else {
$self->{loginstring} = "traditional";
}
} elsif ($self->{connect} && ! $self->{username} && ! $self->{password} &&
$self->{connect} =~ /sysdba@(\w+)/) {
# --connect sysdba@bba
$self->{connect} = $1;
$self->{username} = "/";
$self->{sid} = $self->{connect};
$self->{loginstring} = "sysdba";
} elsif ($self->{connect} && ! $self->{username} && ! $self->{password} &&
$self->{connect} =~ /(\w+)/) {
# --connect bba
$self->{connect} = $1;
# maybe this is a os authenticated user
delete $ENV{TWO_TASK};
$self->{sid} = $self->{connect};
if ($^O ne "hpux") { #hpux && 1.21 only accepts "DBI:Oracle:SID"
$self->{connect} = ""; #linux 1.20 only accepts "DBI:Oracle:" + ORACLE_SID
}
$self->{username} = '/';
$self->{password} = "";
$self->{loginstring} = "extauth";
} elsif ($self->{username} &&
$self->{username} =~ /^\/@(\w+)/) {
# --user /@ubba1
$self->{username} = $1;
$self->{sid} = $self->{connect};
$self->{loginstring} = "passwordstore";
} elsif ($self->{connect} && $self->{username} && ! $self->{password} &&
$self->{username} eq "sysdba") {
# --connect bba --user sysdba
$self->{connect} = $1;
$self->{username} = "/";
$self->{sid} = $self->{connect};
$self->{loginstring} = "sysdba";
} elsif ($self->{connect} && $self->{username} && $self->{password}) {
# --connect bba --username nagios --password oradbmon
$self->{sid} = $self->{connect};
$self->{loginstring} = "traditional";
} else {
$self->{errstr} = "Please specify database, username and password";
return undef;
}
}
if (! exists $self->{errstr}) {
eval {
$ENV{ORACLE_SID} = $self->{sid};
$ENV{PATH} = $ENV{ORACLE_HOME}."/bin".
(defined $ENV{PATH} ?
":".$ENV{PATH} : "");
$ENV{LD_LIBRARY_PATH} = $ENV{ORACLE_HOME}."/lib".
(defined $ENV{LD_LIBRARY_PATH} ?
":".$ENV{LD_LIBRARY_PATH} : "");
# am 30.9.2008 hat perl das /bin/sqlplus in $ENV{ORACLE_HOME}.'/bin/sqlplus'
# eiskalt evaluiert und
# /u00/app/oracle/product/11.1.0/db_1/u00/app/oracle/product/11.1.0/db_1/bin/sqlplus
# draus gemacht. Leider nicht in Mini-Scripts reproduzierbar, sondern nur hier.
# Das ist der Grund fuer die vielen ' und .
my $sqlplus = $ENV{ORACLE_HOME}.'/'.'bin'.'/'.'sqlplus';
if ((-x $ENV{ORACLE_HOME}.'/'.'sqlplus') && ( -f $ENV{ORACLE_HOME}.'/'.'sqlplus')) {
$sqlplus = $ENV{ORACLE_HOME}.'/'.'sqlplus';
}
my $tnsping = $ENV{ORACLE_HOME}.'/'.'bin'.'/'.'tnsping';
if (! -x $sqlplus) {
die "nosqlplus\n";
}
if ($self->{mode} =~ /^server::tnsping/) {
if ($self->{loginstring} eq "traditional") {
$self->{sqlplus} = sprintf "%s -S %s/%s@%s < /dev/null",
$sqlplus,
$self->{username}, $self->{password}, $self->{sid};
} elsif ($self->{loginstring} eq "extauth") {
$self->{sqlplus} = sprintf "%s -S / < /dev/null",
$sqlplus;
} elsif ($self->{loginstring} eq "passwordstore") {
$self->{sqlplus} = sprintf "%s -S /@%s < /dev/null",
$sqlplus,
$self->{username};
} elsif ($self->{loginstring} eq "sysdba") {
$self->{sqlplus} = sprintf "%s -S / as sysdba < /dev/null",
$sqlplus;
} elsif ($self->{loginstring} eq "sys") {
$self->{sqlplus} = sprintf "%s -S %s/%s@%s as sysdba < /dev/null",
$sqlplus,
$self->{username}, $self->{password}, $self->{sid};
}
} else {
if ($self->{loginstring} eq "traditional") {
$self->{sqlplus} = sprintf "%s -S %s/%s@%s < %s > %s",
$sqlplus,
$self->{username}, $self->{password}, $self->{sid},
$self->{sql_commandfile}, $self->{sql_resultfile};
} elsif ($self->{loginstring} eq "extauth") {
$self->{sqlplus} = sprintf "%s -S / < %s > %s",
$sqlplus,
$self->{sql_commandfile}, $self->{sql_resultfile};
} elsif ($self->{loginstring} eq "passwordstore") {
$self->{sqlplus} = sprintf "%s -S /@%s < %s > %s",
$sqlplus,
$self->{username},
$self->{sql_commandfile}, $self->{sql_resultfile};
} elsif ($self->{loginstring} eq "sysdba") {
$self->{sqlplus} = sprintf "%s -S / as sysdba < %s > %s",
$sqlplus,
$self->{sql_commandfile}, $self->{sql_resultfile};
} elsif ($self->{loginstring} eq "sys") {
$self->{sqlplus} = sprintf "%s -S %s/%s@%s as sysdba < %s > %s",
$sqlplus,
$self->{username}, $self->{password}, $self->{sid},
$self->{sql_commandfile}, $self->{sql_resultfile};
}
}
use POSIX ':signal_h';
local $SIG{'ALRM'} = sub {
die "alarm\n";
};
my $mask = POSIX::SigSet->new( SIGALRM );
my $action = POSIX::SigAction->new(
sub { die "alarm\n" ; }, $mask);
my $oldaction = POSIX::SigAction->new();
sigaction(SIGALRM ,$action ,$oldaction );
alarm($self->{timeout} - 1); # 1 second before the global unknown timeout
if ($self->{mode} =~ /^server::tnsping/) {
if (-x $tnsping) {
my $exit_output = `$tnsping $self->{sid}`;
if ($?) {
# printf STDERR "tnsping exit bumm \n";
# immer 1 bei misserfolg
}
if ($exit_output =~ /^OK \(\d+/m) {
die "ORA-01017"; # fake a successful connect with wrong password
} elsif ($exit_output =~ /^(TNS\-\d+)/m) {
die $1;
} else {
die "TNS-03505";
}
} else {
my $exit_output = `$self->{sqlplus}`;
if ($?) {
printf STDERR "ping exit bumm \n";
}
$exit_output =~ s/\n//g;
$exit_output =~ s/at $0//g;
chomp $exit_output;
die $exit_output;
}
} else {
my $answer = $self->fetchrow_array(
q{ SELECT 42 FROM dual});
die unless defined $answer and $answer == 42;
}
$retval = $self;
};
if ($@) {
$self->{errstr} = $@;
$self->{errstr} =~ s/at $0 .*//g;
chomp $self->{errstr};
$retval = undef;
}
}
$self->{tac} = Time::HiRes::time();
return $retval;
}
sub fetchrow_array {
my $self = shift;
my $sql = shift;
my @arguments = @_;
my $sth = undef;
my @row = ();
$self->trace(sprintf "fetchrow_array: %s", $sql);
$self->trace(sprintf "args: %s", Data::Dumper::Dumper(\@arguments));
foreach (@arguments) {
# replace the ? by the parameters
if (/^\d+$/) {
$sql =~ s/\?/$_/;
} else {
$sql =~ s/\?/'$_'/;
}
}
$self->create_commandfile($sql);
my $exit_output = `$self->{sqlplus}`;
if ($?) {
printf STDERR "fetchrow_array exit bumm \n";
my $output = do { local (@ARGV, $/) = $self->{sql_resultfile}; <> };
my @oerrs = map {
/(ORA\-\d+:.*)/ ? $1 : ();
} split(/\n/, $output);
$self->{errstr} = join(" ", @oerrs);
} else {
my $output = do { local (@ARGV, $/) = $self->{sql_resultfile}; <> };
@row = map { convert($_) }
map { s/^\s+([\.\d]+)$/$1/g; $_ } # strip leading space from numbers
map { s/\s+$//g; $_ } # strip trailing space
split(/\|/, (split(/\n/, $output))[0]);
}
if ($@) {
$self->debug(sprintf "bumm %s", $@);
}
unlink $self->{sql_commandfile};
unlink $self->{sql_resultfile};
return $row[0] unless wantarray;
return @row;
}
sub fetchall_array {
my $self = shift;
my $sql = shift;
my @arguments = @_;
my $sth = undef;
my $rows = undef;
$self->trace(sprintf "fetchall_array: %s", $sql);
$self->trace(sprintf "args: %s", Data::Dumper::Dumper(\@arguments));
foreach (@arguments) {
# replace the ? by the parameters
if (/^\d+$/) {
$sql =~ s/\?/$_/;
} else {
$sql =~ s/\?/'$_'/;
}
}
$self->create_commandfile($sql);
my $exit_output = `$self->{sqlplus}`;
if ($?) {
printf STDERR "fetchrow_array exit bumm %s\n", $exit_output;
my $output = do { local (@ARGV, $/) = $self->{sql_resultfile}; <> };
my @oerrs = map {
/(ORA\-\d+:.*)/ ? $1 : ();
} split(/\n/, $output);
$self->{errstr} = join(" ", @oerrs);
} else {
my $output = do { local (@ARGV, $/) = $self->{sql_resultfile}; <> };
my @rows = map { [
map { convert($_) }
map { s/^\s+([\.\d]+)$/$1/g; $_ }
map { s/\s+$//g; $_ }
split /\|/
] } grep { ! /^\d+ rows selected/ }
grep { ! /^\d+ [Zz]eilen ausgew / }
grep { ! /^Elapsed: / }
grep { ! /^\s*$/ } split(/\n/, $output);
$rows = \@rows;
}
if ($@) {
$self->debug(sprintf "bumm %s", $@);
}
unlink $self->{sql_commandfile};
unlink $self->{sql_resultfile};
return @{$rows};
}
sub func {
my $self = shift;
my $function = shift;
$self->{handle}->func(@_);
}
sub convert {
my $n = shift;
# mostly used to convert numbers in scientific notation
if ($n =~ /^\s*\d+\s*$/) {
return $n;
} elsif ($n =~ /^\s*([-+]?)(\d*[\.,]*\d*)[eE]{1}([-+]?)(\d+)\s*$/) {
my ($vor, $num, $sign, $exp) = ($1, $2, $3, $4);
$n =~ s/E/e/g;
$n =~ s/,/\./g;
$num =~ s/,/\./g;
my $sig = $sign eq '-' ? "." . ($exp - 1 + length $num) : '';
my $dec = sprintf "%${sig}f", $n;
$dec =~ s/\.[0]+$//g;
return $dec;
} elsif ($n =~ /^\s*([-+]?)(\d+)[\.,]*(\d*)\s*$/) {
return $1.$2.".".$3;
} elsif ($n =~ /^\s*(.*?)\s*$/) {
return $1;
} else {
return $n;
}
}
sub execute {
my $self = shift;
my $sql = shift;
eval {
my $sth = $self->{handle}->prepare($sql);
$sth->execute();
};
if ($@) {
printf STDERR "bumm %s\n", $@;
}
}
sub DESTROY {
my $self = shift;
$self->trace("try to clean up command and result files");
unlink $self->{sql_commandfile} if -f $self->{sql_commandfile};
unlink $self->{sql_resultfile} if -f $self->{sql_resultfile};
}
sub create_commandfile {
my $self = shift;
my $sql = shift;
open CMDCMD, "> $self->{sql_commandfile}";
printf CMDCMD "SET HEADING OFF\n";
printf CMDCMD "SET PAGESIZE 0\n";
printf CMDCMD "SET LINESIZE 32767\n";
printf CMDCMD "SET COLSEP '|'\n";
printf CMDCMD "SET TAB OFF\n";
printf CMDCMD "SET FEEDBACK OFF\n";
printf CMDCMD "SET TRIMSPOOL ON\n";
printf CMDCMD "SET NUMFORMAT 9.999999EEEE\n";
printf CMDCMD "SPOOL %s\n", $self->{sql_resultfile};
# printf CMDCMD "ALTER SESSION SET NLS_NUMERIC_CHARACTERS='.,';\n/\n";
printf CMDCMD "%s\n/\n", $sql;
printf CMDCMD "EXIT\n";
close CMDCMD;
}
package DBD::Oracle::Server::Connection::Sqlrelay;
use strict;
use Net::Ping;
our @ISA = qw(DBD::Oracle::Server::Connection);
sub init {
my $self = shift;
my %params = @_;
my $retval = undef;
if ($self->{mode} =~ /^server::tnsping/) {
if (! $self->{connect}) {
$self->{errstr} = "Please specify a database";
} else {
if ($self->{connect} =~ /([\.\w]+):(\d+)/) {
$self->{host} = $1;
$self->{port} = $2;
$self->{socket} = "";
} elsif ($self->{connect} =~ /([\.\w]+):([\w\/]+)/) {
$self->{host} = $1;
$self->{socket} = $2;
$self->{port} = "";
}
}
} else {
if (! $self->{connect} || ! $self->{username} || ! $self->{password}) {
if ($self->{connect} && $self->{connect} =~ /(\w+)\/(\w+)@([\.\w]+):(\d+)/) {
$self->{username} = $1;
$self->{password} = $2;
$self->{host} = $3;
$self->{port} = $4;
$self->{socket} = "";
} elsif ($self->{connect} && $self->{connect} =~ /(\w+)\/(\w+)@([\.\w]+):([\w\/]+)/) {
$self->{username} = $1;
$self->{password} = $2;
$self->{host} = $3;
$self->{socket} = $4;
$self->{port} = "";
} else {
$self->{errstr} = "Please specify database, username and password";
return undef;
}
} else {
if ($self->{connect} =~ /([\.\w]+):(\d+)/) {
$self->{host} = $1;
$self->{port} = $2;
$self->{socket} = "";
} elsif ($self->{connect} =~ /([\.\w]+):([\w\/]+)/) {
$self->{host} = $1;
$self->{socket} = $2;
$self->{port} = "";
} else {
$self->{errstr} = "Please specify database, username and password";
return undef;
}
}
}
if (! exists $self->{errstr}) {
eval {
require DBI;
use POSIX ':signal_h';
local $SIG{'ALRM'} = sub {
die "alarm\n";
};
my $mask = POSIX::SigSet->new( SIGALRM );
my $action = POSIX::SigAction->new(
sub { die "alarm\n" ; }, $mask);
my $oldaction = POSIX::SigAction->new();
sigaction(SIGALRM ,$action ,$oldaction );
alarm($self->{timeout} - 1); # 1 second before the global unknown timeout
if ($self->{handle} = DBI->connect(
sprintf("DBI:SQLRelay:host=%s;port=%d;socket=%s", $self->{host}, $self->{port}, $self->{socket}),
$self->{username},
$self->{password},
{ RaiseError => 1, AutoCommit => 0, PrintError => 1 })) {
$self->{handle}->do(q{
ALTER SESSION SET NLS_NUMERIC_CHARACTERS=".," });
$retval = $self;
if ($self->{mode} =~ /^server::tnsping/ && $self->{handle}->ping()) {
# database connected. fake a "unknown user"
$self->{errstr} = "ORA-01017";
}
} else {
$self->{errstr} = DBI::errstr();
}
};
if ($@) {
$self->{errstr} = $@;
$self->{errstr} =~ s/at [\w\/\.]+ line \d+.*//g;
$retval = undef;
}
}
$self->{tac} = Time::HiRes::time();
return $retval;
}
sub fetchrow_array {
my $self = shift;
my $sql = shift;
my @arguments = @_;
my $sth = undef;
my @row = ();
$self->trace(sprintf "fetchrow_array: %s", $sql);
$self->trace(sprintf "args: %s", Data::Dumper::Dumper(\@arguments));
eval {
$sth = $self->{handle}->prepare($sql);
if (scalar(@arguments)) {
$sth->execute(@arguments);
} else {
$sth->execute();
}
@row = $sth->fetchrow_array();
};
if ($@) {
$self->debug(sprintf "bumm %s", $@);
}
if (-f "/tmp/check_oracle_health_simulation/".$self->{mode}) {
my $simulation = do { local (@ARGV, $/) =
"/tmp/check_oracle_health_simulation/".$self->{mode}; <> };
@row = split(/\s+/, (split(/\n/, $simulation))[0]);
}
return $row[0] unless wantarray;
return @row;
}
sub fetchall_array {
my $self = shift;
my $sql = shift;
my @arguments = @_;
my $sth = undef;
my $rows = undef;
$self->trace(sprintf "fetchall_array: %s", $sql);
$self->trace(sprintf "args: %s", Data::Dumper::Dumper(\@arguments));
eval {
$sth = $self->{handle}->prepare($sql);
if (scalar(@arguments)) {
$sth->execute(@arguments);
} else {
$sth->execute();
}
$rows = $sth->fetchall_arrayref();
};
if ($@) {
printf STDERR "bumm %s\n", $@;
}
if (-f "/tmp/check_oracle_health_simulation/".$self->{mode}) {
my $simulation = do { local (@ARGV, $/) =
"/tmp/check_oracle_health_simulation/".$self->{mode}; <> };
@{$rows} = map { [ split(/\s+/, $_) ] } split(/\n/, $simulation);
}
return @{$rows};
}
sub func {
my $self = shift;
$self->{handle}->func(@_);
}
sub execute {
my $self = shift;
my $sql = shift;
eval {
my $sth = $self->{handle}->prepare($sql);
$sth->execute();
};
if ($@) {
printf STDERR "bumm %s\n", $@;
}
}
sub DESTROY {
my $self = shift;
#$self->trace(sprintf "disconnecting DBD %s",
# $self->{handle} ? "with handle" : "without handle");
#$self->{handle}->disconnect() if $self->{handle};
}
package main;
use strict;
use Getopt::Long qw(:config no_ignore_case);
use File::Basename;
use lib dirname($0);
use vars qw ($PROGNAME $REVISION $CONTACT $TIMEOUT $STATEFILESDIR $needs_restart %commandline);
$PROGNAME = "check_oracle_health";
$REVISION = '$Revision: 1.6.3 $';
$CONTACT = 'gerhard.lausser@consol.de';
$TIMEOUT = 60;
$STATEFILESDIR = '/var/tmp/check_oracle_health';
$needs_restart = 0;
my @modes = (
['server::tnsping',
'tnsping', undef,
'Check the reachability of the server' ],
['server::connectiontime',
'connection-time', undef,
'Time to connect to the server' ],
['server::instance::connectedusers',
'connected-users', undef,
'Number of currently connected users' ],
['server::instance::blockingsessions',
'blocking-sessions', undef,
'Number of currently blocking-sessions' ],
['server::instance::sessionnum',
'session_num', undef,
'Number of currently session_num' ],
['server::instance::locktimes',
'lock-times', undef,
'Number of currently lock-times' ],
['server::instance::locknumbers',
'lock-numbers', undef,
'Number of currently lock-numbers' ],
['server::instance::longtransactions',
'long-transactions', undef,
'Number of currently long-transactions' ],
['server::instance::usedspaces',
'used-spaces', undef,
'Number of currently used-spaces' ],
['server::instance::sga::databuffer::hitratio',
'sga-data-buffer-hit-ratio', undef,
'Data Buffer Cache Hit Ratio' ],
['server::instance::sga::sharedpool::librarycache::hitratio',
'sga-library-cache-hit-ratio', undef,
'Library Cache Hit Ratio' ],
['server::instance::sga::sharedpool::dictionarycache::hitratio',
'sga-dictionary-cache-hit-ratio', undef,
'Dictionary Cache Hit Ratio' ],
['server::instance::sga::latch::hitratio',
'sga-latches-hit-ratio', undef,
'Latches Hit Ratio' ],
['server::instance::sga::sharedpool::reloads',
'sga-shared-pool-reload-ratio', ['sga-shared-pool-reloads'],
'Shared Pool Reloads vs. Pins' ],
['server::instance::sga::sharedpool::free',
'sga-shared-pool-free', undef,
'Shared Pool Free Memory' ],
['server::instance::pga::inmemorysortratio',
'pga-in-memory-sort-ratio', undef,
'PGA in-memory sort ratio' ],
['server::database::invalidobjects',
'invalid-objects', undef,
'Number of invalid objects in database' ],
['server::database::stalestats',
'stale-statistics', undef,
'Find objects with stale optimizer statistics' ],
['server::database::tablespace::usage',
'tablespace-usage', undef,
'Used space in tablespaces' ],
['server::database::tablespace::free',
'tablespace-free', undef,
'Free space in tablespaces' ],
['server::database::tablespace::remainingfreetime',
'tablespace-remaining-time', undef,
'Remaining time until a tablespace is full' ],
['server::database::tablespace::fragmentation',
'tablespace-fragmentation', undef,
'Free space fragmentation index' ],
['server::database::tablespace::iobalance',
'tablespace-io-balance', undef,
'balanced io of all datafiles' ],
['server::database::tablespace::segment::extendspace',
'tablespace-can-allocate-next', undef,
'Segments (of a tablespace) can allocate next extent' ],
['server::database::tablespace::datafile::iotraffic',
'datafile-io-traffic', undef,
'io operations/per sec of a datafile' ],
['server::instance::sga::sharedpool::softparse',
'soft-parse-ratio', undef,
'Percentage of soft parses' ],
['server::instance::sga::redologbuffer::switchinterval',
'switch-interval', ['redo-switch-interval', 'rac-switch-interval'],
'Time between redo log file switches' ],
['server::instance::sga::redologbuffer::retryratio',
'retry-ratio', ['redo-retry-ratio'],
'Redo buffer allocation retries' ],
['server::instance::sga::redologbuffer::iotraffic',
'redo-io-traffic', undef,
'Redo log io bytes per second' ],
['server::instance::sga::rollbacksegments::headercontention',
'roll-header-contention', undef,
'Rollback segment header contention' ],
['server::instance::sga::rollbacksegments::blockcontention',
'roll-block-contention', undef,
'Rollback segment block contention' ],
['server::instance::sga::rollbacksegments::hitratio',
'roll-hit-ratio', undef,
'Rollback segment hit ratio (gets/waits)' ],
['server::instance::sga::rollbacksegments::wraps',
'roll-wraps', undef,
'Rollback segment wraps (per sec)' ],
['server::instance::sga::rollbacksegments::extends',
'roll-extends', undef,
'Rollback segment extends (per sec)' ],
['server::instance::sga::rollbacksegments::avgactivesize',
'roll-avgactivesize', undef,
'Rollback segment average active size' ],
['server::database::tablespace::segment::top10logicalreads',
'seg-top10-logical-reads', undef,
'user objects among top 10 logical reads' ],
['server::database::tablespace::segment::top10physicalreads',
'seg-top10-physical-reads', undef,
'user objects among top 10 physical reads' ],
['server::database::tablespace::segment::top10bufferbusywaits',
'seg-top10-buffer-busy-waits', undef,
'user objects among top 10 buffer busy waits' ],
['server::database::tablespace::segment::top10rowlockwaits',
'seg-top10-row-lock-waits', undef,
'user objects among top 10 row lock waits' ],
['server::instance::event::waits',
'event-waits', undef,
'processes wait events' ],
['server::instance::event::waiting',
'event-waiting', undef,
'time spent by processes waiting for an event' ],
['server::instance::enqueue::contention',
'enqueue-contention', undef,
'percentage of enqueue requests which must wait' ],
['server::instance::enqueue::waiting',
'enqueue-waiting', undef,
'percentage of time spent waiting for the enqueue' ],
['server::instance::sga::latch::contention',
'latch-contention', undef,
'percentage of latch get requests which must wait' ],
['server::instance::sga::latch::waiting',
'latch-waiting', undef,
'percentage of time a latch spends sleeping' ],
['server::instance::sysstat::rate',
'sysstat', undef,
'change of sysstat values over time' ],
['server::sql',
'sql', undef,
'any sql command returning a single number' ],
['server::database::tablespace::listtablespaces',
'list-tablespaces', undef,
'convenience function which lists all tablespaces' ],
['server::database::tablespace::datafile::listdatafiles',
'list-datafiles', undef,
'convenience function which lists all datafiles' ],
['server::instance::enqueue::listenqueues',
'list-enqueues', undef,
'convenience function which lists all enqueues' ],
['server::instance::sga::latch::listlatches',
'list-latches', undef,
'convenience function which lists all latches' ],
['server::instance::event::listevents',
'list-events', undef,
'convenience function which lists all events' ],
['server::instance::event::listeventsbg',
'list-background-events', undef,
'convenience function which lists all background events' ],
['server::instance::sysstat::listsysstats',
'list-sysstats', undef,
'convenience function which lists all statistics from v$sysstat' ],
);
sub print_usage () {
print <
Usage:
$PROGNAME [-v] [-t] --connect=
--username= --password= --mode=
--tablespace=
$PROGNAME [-h | --help]
$PROGNAME [-V | --version]
Options:
--connect
the connect string
--username
the oracle user
--password
the oracle user's password
--warning
the warning range
--critical
the critical range
--mode
the mode of the plugin. select one of the following keywords:
EOUS
my $longest = length ((reverse sort {length $a <=> length $b} map { $_->[1] } @modes)[0]);
my $format = " %-".
(length ((reverse sort {length $a <=> length $b} map { $_->[1] } @modes)[0])).
"s\t(%s)\n";
foreach (@modes) {
printf $format, $_->[1], $_->[3];
}
printf "\n";
print <
--name
the name of the tablespace, datafile, wait event,
latch, enqueue, or sql statement depending on the mode.
--name2
if name is a sql statement, this statement would appear in
the output and the performance data. This can be ugly, so
name2 can be used to appear instead.
--regexp
if this parameter is used, name will be interpreted as a
regular expression.
--units
one of %, KB, MB, GB. This is used for a better output of mode=sql
and for specifying thresholds for mode=tablespace-free
Tablespace-related modes check all tablespaces in one run by default.
If only a single tablespace should be checked, use the --name parameter.
The same applies to datafile-related modes.
tablespace-remaining-time will take historical data into account. The number
of days in the past can be given with the --lookback parameter. (Default: 30)
In mode sql you can url-encode the statement so you will not have to mess
around with special characters in your Nagios service definitions.
Instead of
--name="select count(*) from v\$session where status = 'ACTIVE'"
you can say
--name=select%20count%28%2A%29%20from%20v%24session%20where%20status%20%3D%20%27ACTIVE%27
For your convenience you can call check_oracle_health with the --encode
option and it will encode the standard input.
EOUS
#
# --basis
# one of rate, delta, value
}
sub print_help () {
print "Copyright (c) 2008 Gerhard Lausser\n\n";
print "\n";
print " Check various parameters of Oracle databases \n";
print "\n";
print_usage();
support();
}
sub print_revision ($$) {
my $commandName = shift;
my $pluginRevision = shift;
$pluginRevision =~ s/^\$Revision: //;
$pluginRevision =~ s/ \$\s*$//;
print "$commandName ($pluginRevision)\n";
print "This nagios plugin comes with ABSOLUTELY NO WARRANTY. You may redistribute\ncopies of this plugin under the terms of the GNU General Public License.\n";
}
sub support () {
my $support='Send email to gerhard.lausser@consol.de if you have questions\nregarding use of this software. \nPlease include version information with all correspondence (when possible,\nuse output from the --version option of the plugin itself).\n';
$support =~ s/@/\@/g;
$support =~ s/\\n/\n/g;
print $support;
}
sub contact_author ($$) {
my $item = shift;
my $strangepattern = shift;
if ($commandline{verbose}) {
printf STDERR
"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n".
"You found a line which is not recognized by %s\n".
"This means, certain components of your system cannot be checked.\n".
"Please contact the author %s and\nsend him the following output:\n\n".
"%s /%s/\n\nThank you!\n".
"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n",
$PROGNAME, $CONTACT, $item, $strangepattern;
}
}
%commandline = ();
my @params = (
"timeout|t=i",
"version|V",
"help|h",
"verbose|v",
"debug|d",
"connect|c=s",
"username|u=s",
"password|p=s",
"mode|m=s",
"tablespace=s",
"datafile=s",
"waitevent=s",
"name=s",
"name2=s",
"regexp",
"perfdata",
"warning=s",
"critical=s",
"absolute|a",
"basis",
"lookback|l=i",
"environment|e=s%",
"method=s",
"runas|r=s",
"scream",
"shell",
"eyecandy",
"encode",
"units=s",
"3");
if (! GetOptions(\%commandline, @params)) {
print_help();
exit $ERRORS{UNKNOWN};
}
if (exists $commandline{version}) {
print_revision($PROGNAME, $REVISION);
exit $ERRORS{OK};
}
if (exists $commandline{help}) {
print_help();
exit $ERRORS{OK};
} elsif (! exists $commandline{mode}) {
printf "Please select a mode\n";
print_help();
exit $ERRORS{OK};
}
if ($commandline{mode} eq "encode") {
my $input = <>;
chomp $input;
$input =~ s/([^A-Za-z0-9])/sprintf("%%%02X", ord($1))/seg;
printf "%s\n", $input;
exit $ERRORS{OK};
}
if (exists $commandline{3}) {
$ENV{NRPE_MULTILINESUPPORT} = 1;
}
if (exists $commandline{timeout}) {
$TIMEOUT = $commandline{timeout};
}
if (exists $commandline{verbose}) {
$DBD::Oracle::Server::verbose = exists $commandline{verbose};
}
if (exists $commandline{scream}) {
# $DBD::Oracle::Server::hysterical = exists $commandline{scream};
}
if (exists $commandline{method}) {
# dbi, snmp or sqlplus
} else {
$commandline{method} = "dbi";
}
$DBD::Oracle::Server::my_modules_dyn_dir = '/usr/local/nagios/libexec';
if (exists $commandline{environment}) {
# if the desired environment variable values are different from
# the environment of this running script, then a restart is necessary.
# because setting $ENV does _not_ change the environment of the running script.
foreach (keys %{$commandline{environment}}) {
if ((! $ENV{$_}) || ($ENV{$_} ne $commandline{environment}->{$_})) {
$needs_restart = 1;
$ENV{$_} = $commandline{environment}->{$_};
printf STDERR "new %s=%s forces restart\n", $_, $ENV{$_}
if $DBD::Oracle::Server::verbose;
}
}
# e.g. called with --runas dbnagio. shlib_path environment variable is stripped
# during the sudo.
# so the perl interpreter starts without a shlib_path. but --runas cares for
# a --environment shlib_path=...
# so setting the environment variable in the code above and restarting the
# perl interpreter will help it find shared libs
}
if (exists $commandline{runas}) {
# remove the runas parameter
# exec sudo $0 ... the remaining parameters
$needs_restart = 1;
# if the calling script has a path for shared libs and there is no --environment
# parameter then the called script surely needs the variable too.
foreach my $important_env qw(LD_LIBRARY_PATH SHLIB_PATH
ORACLE_HOME TNS_ADMIN ORA_NLS ORA_NLS33 ORA_NLS10) {
if ($ENV{$important_env} && ! scalar(grep { /^$important_env=/ }
keys %{$commandline{environment}})) {
$commandline{environment}->{$important_env} = $ENV{$important_env};
printf STDERR "add important --environment %s=%s\n",
$important_env, $ENV{$important_env} if $DBD::Oracle::Server::verbose;
}
}
}
if ($needs_restart) {
my @newargv = ();
my $runas = undef;
if (exists $commandline{runas}) {
$runas = $commandline{runas};
delete $commandline{runas};
}
foreach my $option (keys %commandline) {
if (grep { /^$option/ && /=/ } @params) {
if (ref ($commandline{$option}) eq "HASH") {
foreach (keys %{$commandline{$option}}) {
push(@newargv, sprintf "--%s", $option);
push(@newargv, sprintf "%s=%s", $_, $commandline{$option}->{$_});
}
} else {
push(@newargv, sprintf "--%s", $option);
push(@newargv, sprintf "%s", $commandline{$option});
}
} else {
push(@newargv, sprintf "--%s", $option);
}
}
if ($runas && ($> == 0)) {
# this was not my idea. some people connect as root to their nagios clients.
exec "su", "-c", sprintf("%s %s", $0, join(" ", @newargv)), "-", $runas;
} elsif ($runas) {
exec "sudo", "-S", "-u", $runas, $0, @newargv;
} else {
exec $0, @newargv;
# this makes sure that even a SHLIB or LD_LIBRARY_PATH are set correctly
# when the perl interpreter starts. Setting them during runtime does not
# help loading e.g. libclntsh.so
}
exit;
}
if (exists $commandline{shell}) {
# forget what you see here.
system("/bin/sh");
}
if (exists $commandline{name}) {
# objects can be encoded like an url
# with s/([^A-Za-z0-9])/sprintf("%%%02X", ord($1))/seg;
if (($commandline{mode} ne "sql") ||
(($commandline{mode} eq "sql") &&
($commandline{name} =~ /select%20/i))) { # protect ... like '%cac%' ... from decoding
$commandline{name} =~ s/\%([A-Fa-f0-9]{2})/pack('C', hex($1))/seg;
}
if ($commandline{name} =~ /^0$/) {
# without this, $params{selectname} would be treated like undef
$commandline{name} = "00";
}
}
$SIG{'ALRM'} = sub {
printf "UNKNOWN - %s timed out after %d seconds\n", $PROGNAME, $TIMEOUT;
exit $ERRORS{UNKNOWN};
};
alarm($TIMEOUT);
my $nagios_level = $ERRORS{UNKNOWN};
my $nagios_message = "";
my $perfdata = "";
my $racmode = 0;
if ($commandline{mode} =~ /^rac-([^\-.]+)/) {
$racmode = 1;
$commandline{mode} =~ s/^rac\-//g;
}
if ($commandline{mode} =~ /^my-([^\-.]+)/) {
my $param = $commandline{mode};
$param =~ s/\-/::/g;
push(@modes, [$param, $commandline{mode}, undef, 'my extension']);
} elsif ((! grep { $commandline{mode} eq $_ } map { $_->[1] } @modes) &&
(! grep { $commandline{mode} eq $_ } map { defined $_->[2] ? @{$_->[2]} : () } @modes)) {
printf "UNKNOWN - mode %s\n", $commandline{mode};
print_usage();
exit 3;
}
my %params = (
timeout => $TIMEOUT,
mode => (
map { $_->[0] }
grep {
($commandline{mode} eq $_->[1]) ||
( defined $_->[2] && grep { $commandline{mode} eq $_ } @{$_->[2]})
} @modes
)[0],
racmode => $racmode,
method => $commandline{method} ||
$ENV{NAGIOS__SERVICEORACLE_METH} ||
$ENV{NAGIOS__HOSTORACLE_METH} || 'dbi',
connect => $commandline{connect} ||
$ENV{NAGIOS__SERVICEORACLE_SID} ||
$ENV{NAGIOS__HOSTORACLE_SID} ||
$ENV{ORACLE_SID},
username => $commandline{username} ||
$ENV{NAGIOS__SERVICEORACLE_USER} ||
$ENV{NAGIOS__HOSTORACLE_USER},
password => $commandline{password} ||
$ENV{NAGIOS__SERVICEORACLE_PASS} ||
$ENV{NAGIOS__HOSTORACLE_PASS},
warningrange => $commandline{warning},
criticalrange => $commandline{critical},
absolute => $commandline{absolute},
lookback => $commandline{lookback},
tablespace => $commandline{tablespace},
datafile => $commandline{datafile},
basis => $commandline{basis},
selectname => $commandline{name} || $commandline{tablespace} || $commandline{datafile},
regexp => $commandline{regexp},
name => $commandline{name},
name2 => $commandline{name2} || $commandline{name},
units => $commandline{units},
eyecandy => $commandline{eyecandy},
statefilesdir => $STATEFILESDIR,
);
my $server = undef;
$server = DBD::Oracle::Server->new(%params);
$server->nagios(%params);
$server->calculate_result();
$nagios_message = $server->{nagios_message};
$nagios_level = $server->{nagios_level};
$perfdata = $server->{perfdata};
printf "%s - %s", $ERRORCODES{$nagios_level}, $nagios_message;
printf " | %s", $perfdata if $perfdata;
printf "\n";
exit $nagios_level;
測試命令:
[root@sznagios libexec]# ./ddcheck_oracle_health --connect=ebpdb --user=dbsnmp --password=dbmon1229db --warning 90 --critical 95 --mode lock-numbers
OK - 0 lock numbers | lock_numbers=0;90;95
[root@sznagios libexec]#
[root@sznagios libexec]#
[root@sznagios libexec]# ./ddcheck_oracle_health --connect=ebpdb --user=dbsnmp --password=dbmon1229db --warning 90 --critical 95 --mode session_num
CRITICAL - 914 session num | session_num=914;90;95
[root@sznagios libexec]#
指令碼修改部分已用紅色標註:
#! /usr/bin/perl -w
$ENV{"ORACLE_HOME"}="/app/oracle/ora11g";
#$ENV{"NLS_LANG"}="AMERICAN_AMERICA.ZHS16GBK";
my %ERRORS=( OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 );
my %ERRORCODES=( 0 => 'OK', 1 => 'WARNING', 2 => 'CRITICAL', 3 => 'UNKNOWN' );
package DBD::Oracle::Server::Instance::SGA::DataBuffer;
use strict;
our @ISA = qw(DBD::Oracle::Server::Instance::SGA);
sub new {
my $class = shift;
my %params = @_;
my $self = {
handle => $params{handle},
sum_physical_reads => undef,
sum_physical_reads_direct => undef,
sum_physical_reads_direct_lob => undef,
sum_session_logical_reads => undef,
hitratio => undef,
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
};
bless $self, $class;
$self->init(%params);
return $self;
}
sub init {
my $self = shift;
my %params = @_;
$self->init_nagios();
if ($params{mode} =~ /server::instance::sga::databuffer::hitratio/) {
($self->{sum_physical_reads}, $self->{sum_physical_reads_direct},
$self->{sum_physical_reads_direct_lob},
$self->{sum_session_logical_reads}) =
$self->{handle}->fetchrow_array(q{
SELECT SUM(DECODE(name, 'physical reads', value, 0)),
SUM(DECODE(name, 'physical reads direct', value, 0)),
SUM(DECODE(name, 'physical reads direct (lob)', value, 0)),
SUM(DECODE(name, 'session logical reads', value, 0))
FROM sys.v_$sysstat
});
if (! defined $self->{sum_physical_reads}) {
$self->add_nagios_critical("unable to get sga buffer cache");
} else {
$self->valdiff(\%params, qw(sum_physical_reads sum_physical_reads_direct
sum_physical_reads_direct_lob sum_session_logical_reads));
$self->{hitratio} = $self->{delta_sum_session_logical_reads} ?
100 - 100 * ((
$self->{delta_sum_physical_reads} -
$self->{delta_sum_physical_reads_direct_lob} -
$self->{delta_sum_physical_reads_direct}) /
$self->{delta_sum_session_logical_reads}) : 0;
}
}
}
sub nagios {
my $self = shift;
my %params = @_;
if (! $self->{nagios_level}) {
if ($params{mode} =~ /server::instance::sga::databuffer::hitratio/) {
$self->add_nagios(
$self->check_thresholds($self->{hitratio}, "98:", "95:"),
sprintf "SGA data buffer hit ratio %.2f%%", $self->{hitratio});
$self->add_perfdata(sprintf "sga_data_buffer_hit_ratio=%.2f%%;%s;%s",
$self->{hitratio},
$self->{warningrange}, $self->{criticalrange});
}
}
}
package DBD::Oracle::Server::Instance::SGA::SharedPool::DictionaryCache;
use strict;
our @ISA = qw(DBD::Oracle::Server::Instance::SGA::SharedPool);
sub new {
my $class = shift;
my %params = @_;
my $self = {
handle => $params{handle},
sum_gethits => undef,
sum_gets => undef,
hitratio => undef,
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
};
bless $self, $class;
$self->init(%params);
return $self;
}
sub init {
my $self = shift;
my %params = @_;
$self->init_nagios();
if ($params{mode} =~
/server::instance::sga::sharedpool::dictionarycache::hitratio/) {
($self->{sum_gets}, $self->{sum_gethits}) =
$self->{handle}->fetchrow_array(q{
SELECT SUM(gets), SUM(gets-getmisses) FROM v$rowcache
});
if (! defined $self->{sum_gets}) {
$self->add_nagios_critical("unable to get sga dc");
} else {
$self->valdiff(\%params, qw(sum_gets sum_gethits));
$self->{hitratio} = $self->{delta_sum_gets} ?
(100 * $self->{delta_sum_gethits} / $self->{delta_sum_gets}) : 0;
}
}
}
sub nagios {
my $self = shift;
my %params = @_;
if (! $self->{nagios_level}) {
if ($params{mode} =~
/server::instance::sga::sharedpool::dictionarycache::hitratio/) {
$self->add_nagios(
$self->check_thresholds($self->{hitratio}, "95:", "90:"),
sprintf "SGA dictionary cache hit ratio %.2f%%", $self->{hitratio});
$self->add_perfdata(sprintf "sga_dictionary_cache_hit_ratio=%.2f%%;%s;%s",
$self->{hitratio}, $self->{warningrange}, $self->{criticalrange});
}
}
}
package DBD::Oracle::Server::Instance::SGA::SharedPool::LibraryCache;
use strict;
our @ISA = qw(DBD::Oracle::Server::Instance::SGA::SharedPool);
sub new {
my $class = shift;
my %params = @_;
my $self = {
handle => $params{handle},
sum_gets => undef,
sum_gethits => undef,
sum_pins => undef,
sum_pinhits => undef,
get_hitratio => undef,
pin_hitratio => undef,
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
};
bless $self, $class;
$self->init(%params);
return $self;
}
sub init {
my $self = shift;
my %params = @_;
$self->init_nagios();
if ($params{mode} =~
/server::instance::sga::sharedpool::librarycache::hitratio/) {
($self->{sum_gethits}, $self->{sum_gets}, $self->{sum_pinhits},
$self->{sum_pins}) = $self->{handle}->fetchrow_array(q{
SELECT SUM(gethits), SUM(gets), SUM(pinhits), SUM(pins)
FROM v$librarycache
});
if (! defined $self->{sum_gets} || ! defined $self->{sum_pinhits}) {
$self->add_nagios_critical("unable to get sga lc");
} else {
$self->valdiff(\%params, qw(sum_gets sum_gethits sum_pins sum_pinhits));
$self->{get_hitratio} = $self->{delta_sum_gets} ?
(100 * $self->{delta_sum_gethits} / $self->{delta_sum_gets}) : 0;
$self->{pin_hitratio} = $self->{delta_sum_pins} ?
(100 * $self->{delta_sum_pinhits} / $self->{delta_sum_pins}) : 0;
}
}
}
sub nagios {
my $self = shift;
my %params = @_;
if (! $self->{nagios_level}) {
if ($params{mode} =~
/server::instance::sga::sharedpool::librarycache::hitratio/) {
$self->add_nagios(
$self->check_thresholds($self->{get_hitratio}, "98:", "95:"),
sprintf "SGA library cache hit ratio %.2f%%", $self->{get_hitratio});
$self->add_perfdata(sprintf "sga_library_cache_hit_ratio=%.2f%%;%s;%s",
$self->{get_hitratio}, $self->{warningrange}, $self->{criticalrange});
}
}
}
package DBD::Oracle::Server::Instance::SGA::SharedPool;
use strict;
our @ISA = qw(DBD::Oracle::Server::Instance::SGA);
sub new {
my $class = shift;
my %params = @_;
my $self = {
free => undef,
reloads => undef,
pins => undef,
handle => $params{handle},
library_cache => undef,
dictionary_cache => undef,
parse_soft => undef,
parse_hard => undef,
parse_failures => undef,
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
};
bless $self, $class;
$self->init(%params);
return $self;
}
sub init {
my $self = shift;
my %params = @_;
$self->init_nagios();
if ($params{mode} =~ /server::instance::sga::sharedpool::librarycache/) {
$self->{library_cache} =
DBD::Oracle::Server::Instance::SGA::SharedPool::LibraryCache->new(
%params);
} elsif ($params{mode} =~ /server::instance::sga::sharedpool::dictionarycache/) {
$self->{dictionary_cache} =
DBD::Oracle::Server::Instance::SGA::SharedPool::DictionaryCache->new(
%params);
} elsif ($params{mode} eq "server::instance::sga::sharedpool::free") {
$self->init_shared_pool_free(%params);
} elsif ($params{mode} eq "server::instance::sga::sharedpool::reloads") {
$self->init_shared_pool_reloads(%params);
} elsif ($params{mode} eq "server::instance::sga::sharedpool::softparse") {
$self->init_shared_pool_parser(%params);
}
}
sub init_shared_pool_reloads {
my $self = shift;
my %params = @_;
($self->{reloads}, $self->{pins}) = $self->{handle}->fetchrow_array(q{
SELECT SUM(reloads), SUM(pins)
FROM v$librarycache
WHERE namespace IN ('SQL AREA','TABLE/PROCEDURE','BODY','TRIGGER')
});
if (! defined $self->{reloads}) {
$self->add_nagios_critical("unable to get sga reloads");
} else {
$self->valdiff(\%params, qw(reloads pins));
$self->{reload_ratio} = $self->{delta_pins} ?
100 * $self->{delta_reloads} / $self->{delta_pins} : 100;
}
}
sub init_shared_pool_free {
my $self = shift;
my %params = @_;
$self->{free_percent} = $self->{handle}->fetchrow_array(q{
SELECT ROUND((SUM(DECODE(name, 'free memory', bytes, 0)) /
SUM(bytes)) * 100,2) FROM v$sgastat where pool = 'shared pool'
});
# scheint nur bis ora9 sinnvoll zu sein. >10.x liefert 0
#$self->{alloc} = $self->{handle}->fetchrow_array(q{
# SELECT value FROM v$parameter WHERE name = 'shared_pool_size'
#});
if (! defined $self->{free_percent}) {
$self->add_nagios_critical("unable to get sga free");
return undef;
}
}
sub init_shared_pool_parser {
my $self = shift;
my %params = @_;
($self->{parse_total}, $self->{parse_hard}, $self->{parse_failures}) =
$self->{handle}->fetchrow_array(q{
SELECT
(SELECT value FROM v$sysstat WHERE name = 'parse count (total)'),
(SELECT value FROM v$sysstat WHERE name = 'parse count (hard)'),
(SELECT value FROM v$sysstat WHERE name = 'parse count (failures)')
FROM DUAL
});
if (! defined $self->{parse_total}) {
$self->add_nagios_critical("unable to get parser");
} else {
$self->valdiff(\%params, qw(parse_total parse_hard parse_failures));
$self->{parse_soft_ratio} = $self->{delta_parse_total} ?
100 * ($self->{delta_parse_total} - $self->{delta_parse_hard}) /
$self->{delta_parse_total} : 100;
}
}
sub nagios {
my $self = shift;
my %params = @_;
if (! $self->{nagios_level}) {
if ($params{mode} =~ /server::instance::sga::sharedpool::librarycache/) {
$self->{library_cache}->nagios(%params);
$self->merge_nagios($self->{library_cache});
} elsif ($params{mode} =~ /server::instance::sga::sharedpool::dictionarycache/) {
$self->{dictionary_cache}->nagios(%params);
$self->merge_nagios($self->{dictionary_cache});
} elsif ($params{mode} eq "server::instance::sga::sharedpool::free") {
$self->add_nagios(
$self->check_thresholds($self->{free_percent}, "10:", "5:"),
sprintf "SGA shared pool free %.2f%%", $self->{free_percent});
$self->add_perfdata(sprintf "sga_shared_pool_free=%.2f%%;%s;%s",
$self->{free_percent}, $self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} eq "server::instance::sga::sharedpool::reloads") {
$self->add_nagios(
$self->check_thresholds($self->{reload_ratio}, "1", "10"),
sprintf "SGA shared pool reload ratio %.2f%%", $self->{reload_ratio});
$self->add_perfdata(sprintf "sga_shared_pool_reload_ratio=%.2f%%;%s;%s",
$self->{reload_ratio}, $self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} eq "server::instance::sga::sharedpool::softparse") {
$self->add_nagios(
$self->check_thresholds( $self->{parse_soft_ratio}, "98:", "90:"),
sprintf "Soft parse ratio %.2f%%", $self->{parse_soft_ratio});
$self->add_perfdata(sprintf "soft_parse_ratio=%.2f%%;%s;%s",
$self->{parse_soft_ratio},
$self->{warningrange}, $self->{criticalrange});
}
}
}
package DBD::Oracle::Server::Instance::SGA::RollbackSegments;
use strict;
our @ISA = qw(DBD::Oracle::Server::Instance::SGA);
# only create one object with new which stands for all rollback segments
sub new {
my $class = shift;
my %params = @_;
my $self = {
handle => $params{handle},
gets => undef,
waits => undef,
wraps => undef,
extends => undef,
undo_header_waits => undef,
undo_block_waits => undef,
rollback_segment_hit_ratio => undef,
rollback_segment_header_contention => undef,
rollback_segment_block_contention => undef,
rollback_segment_extents => undef,
rollback_segment_wraps => undef,
rollback_segment_wraps_persec => undef,
rollback_segment_extends => undef,
rollback_segment_extends_persec => undef,
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
};
bless $self, $class;
$self->init(%params);
return $self;
}
sub init {
my $self = shift;
my %params = @_;
$self->init_nagios();
if ($params{mode} =~ /server::instance::sga::rollbacksegments::wraps/) {
$self->{wraps} = $self->{handle}->fetchrow_array(q{
SELECT SUM(wraps) FROM v$rollstat
});
if (! defined $self->{wraps}) {
$self->add_nagios_critical("unable to get rollback segments stats");
} else {
$self->valdiff(\%params, qw(wraps));
$self->{rollback_segment_wraps} = $self->{delta_wraps};
$self->{rollback_segment_wraps_persec} = $self->{delta_wraps} /
$self->{delta_timestamp};
}
} elsif ($params{mode} =~
/server::instance::sga::rollbacksegments::extends/) {
$self->{extends} = $self->{handle}->fetchrow_array(q{
SELECT SUM(extends) FROM v$rollstat
});
if (! defined $self->{extends}) {
$self->add_nagios_critical("unable to get rollback segments stats");
} else {
$self->valdiff(\%params, qw(extends));
$self->{rollback_segment_extends} = $self->{delta_extends};
$self->{rollback_segment_extends_persec} = $self->{delta_extends} /
$self->{delta_timestamp};
}
} elsif ($params{mode} =~
/server::instance::sga::rollbacksegments::headercontention/) {
($self->{undo_header_waits}, $self->{waits}) = $self->{handle}->fetchrow_array(q{
SELECT (
SELECT SUM(count)
FROM v$waitstat
WHERE class = 'undo header' OR class = 'system undo header'
) undo, (
SELECT SUM(count)
FROM v$waitstat
) complete
FROM DUAL
});
if (! defined $self->{undo_header_waits}) {
$self->add_nagios_critical("unable to get rollback segments wait stats");
} else {
$self->valdiff(\%params, qw(undo_header_waits waits));
$self->{rollback_segment_header_contention} =
$self->{delta_waits} ? 100 * $self->{delta_undo_header_waits} / $self->{delta_waits} : 0;
}
} elsif ($params{mode} =~
/server::instance::sga::rollbacksegments::blockcontention/) {
($self->{undo_block_waits}, $self->{waits}) = $self->{handle}->fetchrow_array(q{
SELECT (
SELECT SUM(count)
FROM v$waitstat
WHERE class = 'undo block' OR class = 'system undo block'
) undo, (
SELECT SUM(count)
FROM v$waitstat
) complete
FROM DUAL
});
if (! defined $self->{undo_block_waits}) {
$self->add_nagios_critical("unable to get rollback segments wait stats");
} else {
$self->valdiff(\%params, qw(undo_block_waits waits));
$self->{rollback_segment_block_contention} =
$self->{delta_waits} ? 100 * $self->{delta_undo_block_waits} / $self->{delta_waits} : 0;
}
} elsif ($params{mode} =~
/server::instance::sga::rollbacksegments::hitratio/) {
($self->{waits}, $self->{gets}) = $self->{handle}->fetchrow_array(q{
SELECT SUM(waits), SUM(gets) FROM v$rollstat
});
if (! defined $self->{gets}) {
$self->add_nagios_critical("unable to get rollback segments wait stats");
} else {
$self->valdiff(\%params, qw(waits gets));
$self->{rollback_segment_hit_ratio} = $self->{delta_gets} ?
100 - 100 * $self->{delta_waits} / $self->{delta_gets} : 100;
}
} elsif ($params{mode} =~
/server::instance::sga::rollbacksegments::avgactivesize/) {
if ($params{selectname}) {
$self->{rollback_segment_optimization_size} = $self->{handle}->fetchrow_array(q{
SELECT AVG(s.optsize / 1048576) optmization_size
FROM v$rollstat s, v$rollname n
WHERE s.usn = n.usn AND n.name != 'SYSTEM' AND n.name = ?
}, $params{selectname}) || 0;
$self->{rollback_segment_average_active} = $self->{handle}->fetchrow_array(q{
SELECT AVG(s.aveactive / 1048576) average_active
FROM v$rollstat s, v$rollname n
WHERE s.usn = n.usn AND n.name != 'SYSTEM' AND n.name = ?
}, $params{selectname}) || 0;
} else {
$self->{rollback_segment_optimization_size} = $self->{handle}->fetchrow_array(q{
SELECT AVG(s.optsize / 1048576) optmization_size
FROM v$rollstat s, v$rollname n
WHERE s.usn = n.usn AND n.name != 'SYSTEM'
}) || 0;
$self->{rollback_segment_average_active} = $self->{handle}->fetchrow_array(q{
SELECT AVG(s.aveactive / 1048576) average_active
FROM v$rollstat s, v$rollname n
WHERE s.usn = n.usn AND n.name != 'SYSTEM'
}) || 0;
}
} else {
}
}
sub nagios {
my $self = shift;
my %params = @_;
if (! $self->{nagios_level}) {
if ($params{mode} =~ /server::instance::sga::rollbacksegments::wraps/) {
if ($params{absolute}) {
$self->add_nagios(
$self->check_thresholds(
$self->{rollback_segment_wraps}, "1", "100"),
sprintf "Rollback segment wraps %d times",
$self->{rollback_segment_wraps});
} else {
$self->add_nagios(
$self->check_thresholds(
$self->{rollback_segment_wraps_persec}, "1", "100"),
sprintf "Rollback segment wraps %.2f/sec",
$self->{rollback_segment_wraps_persec});
}
$self->add_perfdata(
sprintf "rollback_segment_wraps=%d;%s;%s",
$self->{rollback_segment_wraps},
$self->{warningrange}, $self->{criticalrange});
$self->add_perfdata(
sprintf "rollback_segment_wraps_rate=%.2f;%s;%s",
$self->{rollback_segment_wraps_persec},
$self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~
/server::instance::sga::rollbacksegments::extends/) {
if ($params{absolute}) {
$self->add_nagios(
$self->check_thresholds(
$self->{rollback_segment_extends}, "1", "100"),
sprintf "Rollback segment extends %d times",
$self->{rollback_segment_extends});
} else {
$self->add_nagios(
$self->check_thresholds(
$self->{rollback_segment_extends_persec}, "1", "100"),
sprintf "Rollback segment extends %.2f/sec",
$self->{rollback_segment_extends_persec});
}
$self->add_perfdata(
sprintf "rollback_segment_extends=%d;%s;%s",
$self->{rollback_segment_extends},
$self->{warningrange}, $self->{criticalrange});
$self->add_perfdata(
sprintf "rollback_segment_extends_rate=%.2f;%s;%s",
$self->{rollback_segment_extends_persec},
$self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~
/server::instance::sga::rollbacksegments::headercontention/) {
$self->add_nagios(
$self->check_thresholds(
$self->{rollback_segment_header_contention}, "1", "2"),
sprintf "Rollback segment header contention is %.2f%%",
$self->{rollback_segment_header_contention});
$self->add_perfdata(
sprintf "rollback_segment_header_contention=%.2f%%;%s;%s",
$self->{rollback_segment_header_contention},
$self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~
/server::instance::sga::rollbacksegments::blockcontention/) {
$self->add_nagios(
$self->check_thresholds(
$self->{rollback_segment_block_contention}, "1", "2"),
sprintf "Rollback segment block contention is %.2f%%",
$self->{rollback_segment_block_contention});
$self->add_perfdata(
sprintf "rollback_segment_block_contention=%.2f%%;%s;%s",
$self->{rollback_segment_block_contention},
$self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~
/server::instance::sga::rollbacksegments::hitratio/) {
$self->add_nagios(
$self->check_thresholds(
$self->{rollback_segment_hit_ratio}, "99:", "98:"),
sprintf "Rollback segment hit ratio is %.2f%%",
$self->{rollback_segment_hit_ratio});
$self->add_perfdata(
sprintf "rollback_segment_hit_ratio=%.2f%%;%s;%s",
$self->{rollback_segment_hit_ratio},
$self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~
/server::instance::sga::rollbacksegments::avgactivesize/) {
$self->add_nagios_ok(sprintf "Rollback segment average size %.2f MB",
$self->{rollback_segment_average_active});
$self->add_perfdata(
sprintf "rollback_segment_avgsize=%.2f rollback_segment_optsize=%.2f",
$self->{rollback_segment_average_active},
$self->{rollback_segment_optimization_size});
}
}
}
package DBD::Oracle::Server::Instance::SGA::RedoLogBuffer;
use strict;
our @ISA = qw(DBD::Oracle::Server::Instance::SGA);
sub new {
my $class = shift;
my %params = @_;
my $self = {
handle => $params{handle},
last_switch_interval => undef,
redo_buffer_allocation_retries => undef,
redo_entries => undef,
retry_ratio => undef,
redo_size => undef,
redo_size_per_sec => undef,
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
};
bless $self, $class;
$self->init(%params);
return $self;
}
sub init {
my $self = shift;
my %params = @_;
$self->init_nagios();
if ($params{mode} =~ /server::instance::sga::redologbuffer::switchinterval/) {
if ($self->instance_rac()) {
eval {
# alles was jemals geswitcht hat, letzter switch, zweitletzter switch
# jetzt - letzter switch = mindestlaenge des naechsten intervals
# wenn das lang genug ist, dann war das letzte, kurze intervall
# wohl nur ein ausreisser oder manueller switch
# derzeit laufendes intervall, letztes intervall, vorletztes intervall
($self->{next_switch_interval}, $self->{last_switch_interval}, $self->{nextto_last_switch_interval}) =
$self->{handle}->fetchrow_array(q {
WITH temptab AS
(
SELECT sequence#, first_time FROM sys.v_$log WHERE status = 'CURRENT'
AND thread# = ?
UNION ALL
SELECT sequence#, first_time FROM sys.v_$log_history
WHERE thread# = ?
ORDER BY first_time DESC
)
SELECT
(sysdate - a.first_time) * 1440 * 60 thisinterval,
(a.first_time - b.first_time) * 1440 * 60 lastinterval,
(b.first_time - c.first_time) * 1440 * 60 nexttolastinterval
FROM
(
SELECT NVL(
(
SELECT first_time FROM (
SELECT first_time, rownum AS irow FROM temptab WHERE ROWNUM <= 1
) WHERE irow = 1
) , to_date('20090624','YYYYMMDD')) as first_time FROM dual
) a,
(
SELECT NVL(
(
SELECT first_time FROM (
SELECT first_time, rownum AS irow FROM temptab WHERE ROWNUM <= 2
) WHERE irow = 2
) , to_date('20090624','YYYYMMDD')) as first_time FROM dual
) b,
(
SELECT NVL(
(
SELECT first_time FROM (
SELECT first_time, rownum AS irow FROM temptab WHERE ROWNUM <= 3
) WHERE irow = 3
) , to_date('20090624','YYYYMMDD')) as first_time FROM dual
) c
}, $self->instance_thread(), $self->instance_thread());
};
} else {
eval {
# alles was jemals geswitcht hat, letzter switch, zweitletzter switch
# jetzt - letzter switch = mindestlaenge des naechsten intervals
# wenn das lang genug ist, dann war das letzte, kurze intervall
# wohl nur ein ausreisser oder manueller switch
# derzeit laufendes intervall, letztes intervall, vorletztes intervall
($self->{next_switch_interval}, $self->{last_switch_interval}, $self->{nextto_last_switch_interval}) =
$self->{handle}->fetchrow_array(q {
WITH temptab AS
(
SELECT sequence#, first_time FROM sys.v_$log WHERE status = 'CURRENT'
UNION ALL
SELECT sequence#, first_time FROM sys.v_$log_history ORDER BY first_time DESC
)
SELECT
(sysdate - a.first_time) * 1440 * 60 thisinterval,
(a.first_time - b.first_time) * 1440 * 60 lastinterval,
(b.first_time - c.first_time) * 1440 * 60 nexttolastinterval
FROM
(
SELECT NVL(
(
SELECT first_time FROM (
SELECT first_time, rownum AS irow FROM temptab WHERE ROWNUM <= 1
) WHERE irow = 1
) , to_date('20090624','YYYYMMDD')) as first_time FROM dual
) a,
(
SELECT NVL(
(
SELECT first_time FROM (
SELECT first_time, rownum AS irow FROM temptab WHERE ROWNUM <= 2
) WHERE irow = 2
) , to_date('20090624','YYYYMMDD')) as first_time FROM dual
) b,
(
SELECT NVL(
(
SELECT first_time FROM (
SELECT first_time, rownum AS irow FROM temptab WHERE ROWNUM <= 3
) WHERE irow = 3
) , to_date('20090624','YYYYMMDD')) as first_time FROM dual
) c
});
};
}
if (! defined $self->{last_switch_interval}) {
$self->add_nagios_critical(
sprintf "unable to get last switch interval");
}
} elsif ($params{mode} =~ /server::instance::sga::redologbuffer::retryratio/) {
($self->{redo_buffer_allocation_retries}, $self->{redo_entries}) =
$self->{handle}->fetchrow_array(q{
SELECT a.value, b.value
FROM v$sysstat a, v$sysstat b
WHERE a.name = 'redo buffer allocation retries'
AND b.name = 'redo entries'
});
if (! defined $self->{redo_buffer_allocation_retries}) {
$self->add_nagios_critical("unable to get retry ratio");
} else {
$self->valdiff(\%params, qw(redo_buffer_allocation_retries redo_entries));
$self->{retry_ratio} = $self->{delta_redo_entries} ?
100 * $self->{delta_redo_buffer_allocation_retries} / $self->{delta_redo_entries} : 0;
}
} elsif ($params{mode} =~ /server::instance::sga::redologbuffer::iotraffic/) {
$self->{redo_size} = $self->{handle}->fetchrow_array(q{
SELECT value FROM v$sysstat WHERE name = 'redo size'
});
if (! defined $self->{redo_size}) {
$self->add_nagios_critical("unable to get redo size");
} else {
$self->valdiff(\%params, qw(redo_size));
$self->{redo_size_per_sec} =
$self->{delta_redo_size} / $self->{delta_timestamp};
# Megabytes / sec
$self->{redo_size_per_sec} = $self->{redo_size_per_sec} / 1048576;
}
}
}
sub nagios {
my $self = shift;
my %params = @_;
if (! $self->{nagios_level}) {
if ($params{mode} =~
/server::instance::sga::redologbuffer::switchinterval/) {
my $nextlevel = $self->check_thresholds($self->{next_switch_interval}, "600:", "60:");
my $nexttolastlevel = $self->check_thresholds($self->{nextto_last_switch_interval}, "600:", "60:");
my $lastlevel = $self->check_thresholds($self->{last_switch_interval}, "600:", "60:");
if ($lastlevel) {
# nachschauen, ob sich die situation schon entspannt hat
if ($nextlevel == 2) {
# das riecht nach aerger. kann zwar auch daran liegen, weil der check unmittelbar nach deim kurzen switch
# ausgefuehrt wird, aber dann bleibts beim soft-hard und beim retry schauts schon besser aus.
$self->add_nagios(
# 10: minutes, 1: minute = 600:, 60:
$nextlevel,
sprintf "Last redo log file switch interval was %d minutes%s. Next interval presumably >%d minutes",
$self->{last_switch_interval} / 60,
$self->instance_rac() ? sprintf " (thread %d)", $self->instance_thread() : "",
$self->{next_switch_interval} / 60);
} elsif ($nextlevel == 1) {
# das kommt daher, weil retry_interval < warningthreshold
if ($nexttolastlevel) {
# aber vorher war auch schon was faul. da braut sich vieleicht was zusammen.
# die warnung ist sicher berechtigt.
$self->add_nagios(
$nextlevel,
sprintf "Last redo log file switch interval was %d minutes%s. Next interval presumably >%d minutes. Second incident in a row.",
$self->{last_switch_interval} / 60,
$self->instance_rac() ? sprintf " (thread %d)", $self->instance_thread() : "",
$self->{next_switch_interval} / 60);
} else {
# hier bin ich grosszuegig. vorletztes intervall war ok, letztes intervall war nicht ok.
# ich rechne mir also chancen aus, dass $nextlevel nur auf warning ist, weil der retry zu schnell
# nach dem letzten switch stattfindet. sollte sich entspannen und wenns wirklich ein problem gibt
# dann kommt sowieso wieder ein switch. also erstmal ok.
$self->add_nagios(
0,
sprintf "Last redo log file switch interval was %d minutes%s. Next interval presumably >%d minutes. Probably a single incident.",
$self->{last_switch_interval} / 60,
$self->instance_rac() ? sprintf " (thread %d)", $self->instance_thread() : "",
$self->{next_switch_interval} / 60);
}
} else {
# war wohl ein einzelfall. also gehen wir davon aus, dass das warninglevel nur wegen des retrys
# unterschritten wurde und der naechste switch wieder lange genug sein wird
$self->add_nagios(
$nextlevel, # sollte 0 sein
sprintf "Last redo log file switch interval was %d minutes%s. Next interval presumably >%d minutes",
$self->{last_switch_interval} / 60,
$self->instance_rac() ? sprintf " (thread %d)", $self->instance_thread() : "",
$self->{next_switch_interval} / 60);
}
} else {
$self->add_nagios(
$lastlevel,
sprintf "Last redo log file switch interval was %d minutes%s. Next interval presumably >%d minutes",
$self->{last_switch_interval} / 60,
$self->instance_rac() ? sprintf " (thread %d)", $self->instance_thread() : "",
$self->{next_switch_interval} / 60);
}
$self->add_perfdata(sprintf "redo_log_file_switch_interval=%ds;%s;%s",
$self->{last_switch_interval},
$self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~
/server::instance::sga::redologbuffer::retryratio/) {
$self->add_nagios(
$self->check_thresholds($self->{retry_ratio}, "1", "10"),
sprintf "Redo log retry ratio is %.6f%%",$self->{retry_ratio});
$self->add_perfdata(sprintf "redo_log_retry_ratio=%.6f%%;%s;%s",
$self->{retry_ratio},
$self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~
/server::instance::sga::redologbuffer::iotraffic/) {
$self->add_nagios(
$self->check_thresholds($self->{redo_size_per_sec}, "100", "200"),
sprintf "Redo log io is %.6f MB/sec", $self->{redo_size_per_sec});
$self->add_perfdata(sprintf "redo_log_io_per_sec=%.6f;%s;%s",
$self->{redo_size_per_sec},
$self->{warningrange}, $self->{criticalrange});
}
}
}
package DBD::Oracle::Server::Instance::SGA::Latch;
our @ISA = qw(DBD::Oracle::Server::Instance::SGA);
{
my @latches = ();
my $initerrors = undef;
sub add_latch {
push(@latches, shift);
}
sub return_latches {
my %params = @_;
if ($params{mode} =~ /server::instance::sga::latch::contention/) {
return reverse
sort { $a->{contention} <=> $b->{contention} } @latches;
} else {
return reverse
sort { $a->{name} cmp $b->{name} } @latches;
}
}
sub init_latches {
my %params = @_;
my $num_latches = 0;
if (($params{mode} =~ /server::instance::sga::latch::contention/) ||
($params{mode} =~ /server::instance::sga::latch::waiting/) ||
($params{mode} =~ /server::instance::sga::latch::hitratio/) ||
($params{mode} =~ /server::instance::sga::latch::listlatches/)) {
my $sumsleeps = $params{handle}->fetchrow_array(q{
SELECT SUM(sleeps) FROM v$latch
});
my @latchresult = $params{handle}->fetchall_array(q{
SELECT latch#, name, gets, sleeps, misses, wait_time
FROM v$latch
});
foreach (@latchresult) {
my ($number, $name, $gets, $sleeps, $misses, $wait_time) = @{$_};
if ($params{regexp}) {
next if $params{selectname} && $name !~ /$params{selectname}/;
} else {
next if ($params{selectname} && (
($params{selectname} !~ /^\d+$/ && (lc $params{selectname} ne lc $name)) ||
($params{selectname} =~ /^\d+$/ && ($params{selectname} != $number))));
}
my %thisparams = %params;
$thisparams{number} = $number;
$thisparams{name} = $name;
$thisparams{gets} = $gets;
$thisparams{misses} = $misses;
$thisparams{sleeps} = $sleeps;
$thisparams{wait_time} = $wait_time;
$thisparams{sumsleeps} = $sumsleeps;
my $latch = DBD::Oracle::Server::Instance::SGA::Latch->new(
%thisparams);
add_latch($latch);
$num_latches++;
}
if (! $num_latches) {
$initerrors = 1;
return undef;
}
}
}
}
sub new {
my $class = shift;
my %params = @_;
my $self = {
handle => $params{handle},
number => $params{number},
name => $params{name},
gets => $params{gets},
misses => $params{misses},
sleeps => $params{sleeps},
wait_time => $params{wait_time},
sumsleeps => $params{sumsleeps},
hitratio => undef,
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
};
bless $self, $class;
$self->init(%params);
return $self;
}
sub init {
my $self = shift;
my %params = @_;
$self->init_nagios();
if ($params{mode} =~
/server::instance::sga::latch::hitratio/) {
if (! defined $self->{gets}) {
$self->add_nagios_critical(
sprintf "unable to get sga latches %s", $self->{name});
} else {
$params{differenciator} = lc $self->{name}.$self->{number};
$self->valdiff(\%params, qw(gets misses));
$self->{hitratio} = $self->{delta_gets} ?
100 * ($self->{delta_gets} - $self->{delta_misses}) / $self->{delta_gets} : 100;
}
} elsif (($params{mode} =~ /server::instance::sga::latch::contention/) ||
($params{mode} =~ /server::instance::sga::latch::waiting/)) {
if (! defined $self->{gets}) {
$self->add_nagios_critical(
sprintf "unable to get sga latches %s", $self->{name});
} else {
$params{differenciator} = lc $self->{name}.$self->{number};
$self->valdiff(\%params, qw(gets sleeps misses wait_time sumsleeps));
# latch contention
$self->{contention} = $self->{delta_gets} ?
100 * $self->{delta_misses} / $self->{delta_gets} : 0;
# latch percent of sleep during the elapsed time
$self->{sleep_share} = $self->{delta_wait_time} ?
((100 * $self->{wait_time}) / 1000) / $self->{delta_timestamp} : 0;
}
}
}
sub nagios {
my $self = shift;
my %params = @_;
if (! $self->{nagios_level}) {
if ($params{mode} =~
/server::instance::sga::latch::hitratio/) {
$self->add_nagios(
$self->check_thresholds($self->{hitratio}, "98:", "95:"),
sprintf "SGA latches hit ratio %.2f%%", $self->{hitratio});
$self->add_perfdata(sprintf "sga_latches_hit_ratio=%.2f%%;%s;%s",
$self->{hitratio}, $self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~
/server::instance::sga::latch::contention/) {
$self->add_nagios(
$self->check_thresholds($self->{contention}, "1", "2"),
sprintf "SGA latch %s (#%d) contention %.2f%%",
$self->{name}, $self->{number}, $self->{contention});
$self->add_perfdata(sprintf "'latch_%d_contention'=%.2f%%;%s;%s",
$self->{number}, $self->{contention}, $self->{warningrange}, $self->{criticalrange});
$self->add_perfdata(sprintf "'latch_%d_gets'=%u",
$self->{number}, $self->{delta_gets});
} elsif ($params{mode} =~
/server::instance::sga::latch::waiting/) {
$self->add_nagios(
$self->check_thresholds($self->{sleep_share}, "0.1", "1"),
sprintf "SGA latch %s (#%d) sleeping %.6f%% of the time",
$self->{name}, $self->{number}, $self->{sleep_share});
$self->add_perfdata(sprintf "'latch_%d_sleep_share'=%.6f%%;%s;%s;0;100",
$self->{number}, $self->{sleep_share}, $self->{warningrange}, $self->{criticalrange});
}
}
}
package DBD::Oracle::Server::Instance::SGA;
use strict;
our @ISA = qw(DBD::Oracle::Server::Instance);
sub new {
my $class = shift;
my %params = @_;
my $self = {
handle => $params{handle},
data_buffer => undef,
shared_pool => undef,
latches => undef,
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
};
bless $self, $class;
$self->init(%params);
return $self;
}
sub init {
my $self = shift;
my %params = @_;
$self->init_nagios();
if ($params{mode} =~ /server::instance::sga::databuffer/) {
$self->{data_buffer} = DBD::Oracle::Server::Instance::SGA::DataBuffer->new(
%params);
} elsif ($params{mode} =~ /server::instance::sga::sharedpool/) {
$self->{shared_pool} = DBD::Oracle::Server::Instance::SGA::SharedPool->new(
%params);
} elsif ($params{mode} =~ /server::instance::sga::latch/) {
DBD::Oracle::Server::Instance::SGA::Latch::init_latches(%params);
if (my @latches =
DBD::Oracle::Server::Instance::SGA::Latch::return_latches(%params)) {
$self->{latches} = \@latches;
} else {
$self->add_nagios_critical("unable to aquire latch info");
}
} elsif ($params{mode} =~ /server::instance::sga::redolog/) {
$self->{redo_log_buffer} =
DBD::Oracle::Server::Instance::SGA::RedoLogBuffer->new(%params);
} elsif ($params{mode} =~ /server::instance::sga::rollbacksegments/) {
$self->{rollback_segments} =
DBD::Oracle::Server::Instance::SGA::RollbackSegments->new(%params);
}
}
sub nagios {
my $self = shift;
my %params = @_;
if ($params{mode} =~ /server::instance::sga::databuffer/) {
$self->{data_buffer}->nagios(%params);
$self->merge_nagios($self->{data_buffer});
} elsif ($params{mode} =~ /server::instance::sga::sharedpool/) {
$self->{shared_pool}->nagios(%params);
$self->merge_nagios($self->{shared_pool});
} elsif ($params{mode} =~ /server::instance::sga::latch::hitratio/) {
if (! $self->{nagios_level}) {
my $hitratio = 0;
foreach (@{$self->{latches}}) {
$hitratio = $hitratio + $_->{hitratio};
}
$hitratio = $hitratio / scalar(@{$self->{latches}});
$self->add_nagios(
$self->check_thresholds($hitratio, "98:", "95:"),
sprintf "SGA latches hit ratio %.2f%%", $hitratio);
$self->add_perfdata(sprintf "sga_latches_hit_ratio=%.2f%%;%s;%s",
$hitratio, $self->{warningrange}, $self->{criticalrange});
}
} elsif ($params{mode} =~ /server::instance::sga::latch::listlatches/) {
foreach (sort { $a->{number} <=> $b->{number} } @{$self->{latches}}) {
printf "%03d %s\n", $_->{number}, $_->{name};
}
$self->add_nagios_ok("have fun");
} elsif ($params{mode} =~ /server::instance::sga::latch/) {
foreach (@{$self->{latches}}) {
$_->nagios(%params);
$self->merge_nagios($_);
}
} elsif ($params{mode} =~ /server::instance::sga::redologbuffer/) {
$self->{redo_log_buffer}->nagios(%params);
$self->merge_nagios($self->{redo_log_buffer});
} elsif ($params{mode} =~ /server::instance::sga::rollbacksegments/) {
$self->{rollback_segments}->nagios(%params);
$self->merge_nagios($self->{rollback_segments});
}
}
package DBD::Oracle::Server::Instance::PGA;
use strict;
our @ISA = qw(DBD::Oracle::Server::Instance);
sub new {
my $class = shift;
my %params = @_;
my $self = {
handle => $params{handle},
internals => undef,
pgas => [],
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
};
bless $self, $class;
$self->init(%params);
return $self;
}
sub init {
my $self = shift;
my %params = @_;
$self->init_nagios();
if ($params{mode} =~ /server::instance::pga/) {
$self->{internals} =
DBD::Oracle::Server::Instance::PGA::Internals->new(%params);
}
}
sub nagios {
my $self = shift;
my %params = @_;
if ($params{mode} =~ /server::instance::pga/) {
$self->{internals}->nagios(%params);
$self->merge_nagios($self->{internals});
}
}
package DBD::Oracle::Server::Instance::PGA::Internals;
use strict;
our @ISA = qw(DBD::Oracle::Server::Instance::PGA);
my $internals; # singleton, nur ein einziges mal instantiierbar
sub new {
my $class = shift;
my %params = @_;
unless ($internals) {
$internals = {
handle => $params{handle},
in_memory_sorts => undef,
in_disk_sorts => undef,
in_memory_sort_ratio => undef,
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
};
bless($internals, $class);
$internals->init(%params);
}
return($internals);
}
sub init {
my $self = shift;
my %params = @_;
$self->debug("enter init");
$self->init_nagios();
if ($params{mode} =~ /server::instance::pga::inmemorysortratio/) {
($self->{in_memory_sorts}, $self->{in_disk_sorts}) =
$self->{handle}->fetchrow_array(q{
SELECT mem.value, dsk.value
FROM v$sysstat mem, v$sysstat dsk
WHERE mem.name='sorts (memory)' AND dsk.name='sorts (disk)'
});
if (! defined $self->{in_memory_sorts}) {
$self->add_nagios_critical("unable to get pga ratio");
} else {
$self->valdiff(\%params, qw(in_memory_sorts in_disk_sorts));
$self->{in_memory_sort_ratio} =
($self->{delta_in_memory_sorts} + $self->{delta_in_disk_sorts}) == 0 ? 100 :
100 * $self->{delta_in_memory_sorts} /
($self->{delta_in_memory_sorts} + $self->{delta_in_disk_sorts});
}
}
}
sub nagios {
my $self = shift;
my %params = @_;
if (! $self->{nagios_level}) {
if ($params{mode} =~ /server::instance::pga::inmemorysortratio/) {
$self->add_nagios(
$self->check_thresholds($self->{in_memory_sort_ratio}, "99:", "90:"),
sprintf "PGA in-memory sort ratio %.2f%%",
$self->{in_memory_sort_ratio});
$self->add_perfdata(sprintf "pga_in_memory_sort_ratio=%.2f%%;%s;%s;0;100",
$self->{in_memory_sort_ratio},
$self->{warningrange}, $self->{criticalrange});
}
}
}
package DBD::Oracle::Server::Instance::Event;
use strict;
our @ISA = qw(DBD::Oracle::Server::Instance);
{
my @events = ();
my $initerrors = undef;
sub add_event {
push(@events, shift);
}
sub return_events {
my %params = @_;
if ($params{mode} =~ /server::instance::event::waits/) {
return reverse
sort { $a->{waits_per_sec} <=> $b->{waits_per_sec} } @events;
} elsif ($params{mode} =~ /server::instance::event::waiting/) {
return reverse
sort { $a->{percent_waited} <=> $b->{percent_waited} } @events;
} else {
return reverse
sort { $a->{name} cmp $b->{name} } @events;
}
}
sub init_events {
my %params = @_;
my $num_events = 0;
my %longnames = ();
if (($params{mode} =~ /server::instance::event::wait/) || #waits, waiting
($params{mode} =~ /server::instance::event::listevents/)) {
my $sql;
my @idlewaits = ();
if (DBD::Oracle::Server::return_first_server()->version_is_minimum("10.x")) {
@idlewaits = map { $_->[0] } $params{handle}->fetchall_array(q{
SELECT name FROM v$event_name WHERE wait_class = 'Idle'
});
} elsif (DBD::Oracle::Server::return_first_server()->version_is_minimum("9.x")) {
@idlewaits = (
'smon timer',
'pmon timer',
'rdbms ipc message',
'Null event',
'parallel query dequeue',
'pipe get',
'client message',
'SQL*Net message to client',
'SQL*Net message from client',
'SQL*Net more data from client',
'dispatcher timer',
'virtual circuit status',
'lock manager wait for remote message',
'PX Idle Wait',
'PX Deq: Execution Msg',
'PX Deq: Table Q Normal',
'wakeup time manager',
'slave wait',
'i/o slave wait',
'jobq slave wait',
'null event',
'gcs remote message',
'gcs for action',
'ges remote message',
'queue messages',
);
}
if ($params{mode} =~ /server::instance::event::listeventsbg/) {
if (DBD::Oracle::Server::return_first_server()->version_is_minimum("10.x")) {
$sql = q{
SELECT e.event_id, e.event, 0, 0, 0, 0 FROM v$session_event e WHERE e.sid IN
(SELECT s.sid FROM v$session s WHERE s.type = 'BACKGROUND') GROUP BY e.event, e.event_id
};
} else {
$sql = q{
SELECT n.event#, e.event, 0, 0, 0, 0 FROM v$session_event e, v$event_name n
WHERE n.name = e.event AND e.sid IN
(SELECT s.sid FROM v$session s WHERE s.type = 'BACKGROUND') GROUP BY e.event, n.event#
};
}
} else {
if (DBD::Oracle::Server::return_first_server()->version_is_minimum("10.x")) {
$sql = q{
SELECT e.event_id, e.name,
NVL(s.total_waits, 0), NVL(s.total_timeouts, 0), NVL(s.time_waited, 0),
NVL(s.time_waited_micro, 0), NVL(s.average_wait, 0)
FROM v$event_name e LEFT JOIN sys.v_$system_event s ON e.name = s.event
};
} else {
$sql = q{
SELECT e.event#, e.name,
NVL(s.total_waits, 0), NVL(s.total_timeouts, 0), NVL(s.time_waited, 0),
NVL(s.time_waited_micro, 0), NVL(s.average_wait, 0)
FROM v$event_name e LEFT JOIN sys.v_$system_event s ON e.name = s.event
};
}
}
my @eventresults = $params{handle}->fetchall_array($sql);
foreach (@eventresults) {
my ($event_no, $name, $total_waits, $total_timeouts,
$time_waited, $time_waited_micro, $average_wait) = @{$_};
$longnames{$name} = "";
}
abbreviate(\%longnames, 2);
foreach (@eventresults) {
my ($event_no, $name, $total_waits, $total_timeouts,
$time_waited, $time_waited_micro, $average_wait) = @{$_};
my $shortname = $longnames{$name}->{abbreviation};
if ($params{regexp}) {
next if $params{selectname} && $name !~ /$params{selectname}/;
} else {
next if ($params{selectname} && (
(($params{selectname} !~ /^\d+$/) &&
(! grep /^$params{selectname}$/, map { $longnames{$_}->{abbreviation} }
keys %longnames) &&
(lc $params{selectname} ne lc $name)) ||
(($params{selectname} !~ /^\d+$/) &&
(grep /^$params{selectname}$/, map { $longnames{$_}->{abbreviation} }
keys %longnames) &&
(lc $params{selectname} ne lc $shortname)) ||
($params{selectname} =~ /^\d+$/ &&
($params{selectname} != $event_no))));
}
my %thisparams = %params;
$thisparams{name} = $name;
$thisparams{shortname} = $shortname;
$thisparams{event_id} = $event_no; # bei > 10.x unbedingt event_id aus db holen
$thisparams{total_waits} = $total_waits;
$thisparams{total_timeouts} = $total_timeouts;
$thisparams{time_waited} = $time_waited;
$thisparams{time_waited_micro} = $time_waited_micro;
$thisparams{average_wait} = $average_wait;
$thisparams{idle} = scalar(grep { lc $name =~ /$_/ } @idlewaits);
my $event = DBD::Oracle::Server::Instance::Event->new(
%thisparams);
add_event($event);
$num_events++;
}
if (! $num_events) {
$initerrors = 1;
return undef;
}
}
}
sub begindiff {
# liefere indices fuer das erste untersch. wort und innerhalb diesem das erste untersch. zeichen
my @names = @_;
my $len = 100;
my $first_diff_word = 0;
my $first_diff_pos = 0;
my $smallest_wordcnt = (sort { $a->{wordcnt} <=> $b->{wordcnt} } @names)[0]->{wordcnt};
foreach my $wordno (0..$smallest_wordcnt-1) {
my $wordequal = 1;
my $refword = @{$names[0]->{words}}[$wordno];
foreach (@names) {
if (@{$_->{words}}[$wordno] ne $refword) {
$wordequal = 0;
}
}
$first_diff_word = $wordno;
if (! $wordequal) {
last;
}
}
my $smallest_wordlen =
length(${(sort { length(${$a->{words}}[$first_diff_word]) <=> length(${$b->{words}}[$first_diff_word]) } @names)[0]->{words}}[$first_diff_word]);
foreach my $posno (0..$smallest_wordlen-1) {
my $posequal = 1;
my $refpos = substr(@{$names[0]->{words}}[$first_diff_word], $posno, 1);
foreach (@names) {
if (substr(@{$_->{words}}[$first_diff_word], $posno, 1) ne $refpos) {
$posequal = 0;
}
}
$first_diff_pos = $posno;
if (! $posequal) {
last;
}
}
return ($first_diff_word, $first_diff_pos);
}
sub abbreviate {
#
# => zeiger auf hash, dessen keys lange namen sind
# <= gleicher hash mit ausgefuellten eindeutigen values
#
my $names = shift;
my %done = ();
my $collisions = {};
foreach my $long (keys %{$names}) {
# erstmal das noetige werkzeug schmieden
# und kurzbezeichnungen aus jeweils zwei zeichen bilden
$names->{$long} = {};
$names->{$long}->{words} = [
map { lc }
map { my $x = $_; $x =~ s/[()\/\-]//g; $x }
map { /^\-$/ ? () : $_ }
split(/_|\s+/, $long) ];
$names->{$long}->{wordcnt} = scalar (@{$names->{$long}->{words}});
$names->{$long}->{shortwords} = [ map { substr $_, 0, 2 } @{$names->{$long}->{words}} ];
$names->{$long}->{abbreviation} = join("_", @{$names->{$long}->{shortwords}});
$names->{$long}->{unique} = 1;
}
individualize($names, -1, -1);
}
sub individualize {
my $names = shift;
my $delword = shift;
my $delpos = shift;
my %done = ();
my $collisions = {};
if ($delword >= 0 && $delpos >= 0) {
# delpos ist die position mit dem ersten unterschied. kann fuer den kuerzesten string
# schon nicht mehr existieren.
map {
if (length(${$names->{$_}->{words}}[$delword]) > 2) {
if (length(${$names->{$_}->{words}}[$delword]) == $delpos) {
${$names->{$_}->{shortwords}}[$delword] =
substr(${$names->{$_}->{words}}[$delword], 0, 2)
} else {
${$names->{$_}->{shortwords}}[$delword] =
substr(${$names->{$_}->{words}}[$delword], 0, 1).
substr(${$names->{$_}->{words}}[$delword], $delpos);
}
}
} keys %{$names};
}
map { $names->{$_}->{abbreviation} = join("_", @{$names->{$_}->{shortwords}}) } keys %{$names};
map { $done{$names->{$_}->{abbreviation}}++ } keys %{$names};
map { $names->{$_}->{unique} = $done{$names->{$_}->{abbreviation}} > 1 ? 0 : 1 } keys %{$names};
#
# hash mit abkuerzung als key und array(langnamen, ...) als value.
# diese sind nicht eindeutig und muessen noch geschickter abgekuerzt werden
#
foreach my $collision (map { $names->{$_}->{unique} ? () : $_ } keys %{$names}) {
if (! exists $collisions->{$names->{$collision}->{abbreviation}}) {
$collisions->{$names->{$collision}->{abbreviation}} = [];
}
push(@{$collisions->{$names->{$collision}->{abbreviation}}}, $collision);
}
#
# jeweils gruppen mit gemeinsamer, mehrdeutiger abkuerzung werden nochmals gerechnet
#
foreach my $collision (keys %{$collisions}) {
my $newnames = {};
# hilfestellung, wo es unterschiede gibt
my($wordnum, $posnum) = begindiff(map { $names->{$_} } @{$collisions->{$collision}});
map { $newnames->{$_} =
$names->{$_} } grep { $names->{$_}->{abbreviation} eq $collision } keys %{$names};
individualize($newnames, $wordnum, $posnum);
}
}
}
sub new {
my $class = shift;
my %params = @_;
my $self = {
handle => $params{handle},
name => $params{name},
shortname => $params{shortname},
event_id => $params{event_id}, # > 10.x
total_waits => $params{total_waits},
total_timeouts => $params{total_timeouts},
time_waited => $params{time_waited}, # divide by 100
time_waited_micro => $params{time_waited_micro}, # divide by 1000000
average_wait => $params{average_wait},
idle => $params{idle} || 0,
waits_per_sec => undef,
percent_waited => undef,
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
};
#$self->{name} =~ s/^\s+//;
#$self->{name} =~ s/\s+$//;
bless $self, $class;
$self->init(%params);
return $self;
}
sub init {
my $self = shift;
my %params = @_;
$self->init_nagios();
if ($params{mode} =~ /server::instance::event::wait/) {
if (! defined $self->{total_waits}) {
$self->add_nagios_critical("unable to get event info");
} else {
$params{differenciator} = lc $self->{name};
$self->valdiff(\%params, qw(total_waits total_timeouts time_waited
time_waited_micro average_wait));
$self->{waits_per_sec} =
$self->{delta_total_waits} / $self->{delta_timestamp};
$self->{percent_waited} =
100 * ($self->{delta_time_waited_micro} / 1000000 ) / $self->{delta_timestamp};
}
}
}
sub nagios {
my $self = shift;
my %params = @_;
if (! $self->{nagios_level}) {
if ($params{mode} =~ /server::instance::event::waits/) {
$self->add_nagios(
$self->check_thresholds($self->{waits_per_sec}, "10", "100"),
sprintf "%s : %.6f waits/sec", $self->{name}, $self->{waits_per_sec});
$self->add_perfdata(sprintf "'%s_waits_per_sec'=%.6f;%s;%s",
$self->{name},
$self->{waits_per_sec},
$self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~ /server::instance::event::waiting/) {
$self->add_nagios(
$self->check_thresholds($self->{percent_waited}, "0.1", "0.5"),
sprintf "%s waits %.6f%% of the time", $self->{name}, $self->{percent_waited});
$self->add_perfdata(sprintf "'%s_percent_waited'=%.6f%%;%s;%s",
$self->{name},
$self->{percent_waited},
$self->{warningrange}, $self->{criticalrange});
}
}
}
package DBD::Oracle::Server::Instance::Enqueue;
use strict;
our @ISA = qw(DBD::Oracle::Server::Instance);
{
my @enqueues = ();
my $initerrors = undef;
sub add_enqueue {
push(@enqueues, shift);
}
sub return_enqueues {
return reverse
sort { $a->{name} cmp $b->{name} } @enqueues;
}
sub init_enqueues {
my %params = @_;
my $num_enqueues = 0;
if (($params{mode} =~ /server::instance::enqueue::contention/) ||
($params{mode} =~ /server::instance::enqueue::waiting/) ||
($params{mode} =~ /server::instance::enqueue::listenqueues/)) {
# ora11 PE FP TA DL SR TQ KT PW XR SS SJ SQ IT IA UL WP RR KM
# PD CF SW CT US TD TK JS FS CN DT TS TT JD SE MW AF TL
# PV AS TM TX FB JQ MD TO TH PR RO MR DP WF TB SH RS CU
# AE CI PG IS RT HW DR FU
# ora10 PE FP TA DL SR TQ KT PW XR SS SQ PF IT IA UL WP KM PD
# CF SW CT US TD AG JS DT TS TT CN JD SE MW AF TL PV AS
# TM FB TX JQ MD TO PR RO MR SK DP WF TB SH RS CU AW CI
# PG IS RT HW DR FU
# ora9 CF CI CU DL DP DR DT DX FB HW IA IS IT JD MD MR PE PF
# RO RT SQ SR SS SW TA TD TM TO TS TT TX UL US XR
my @enqueueresults = $params{handle}->fetchall_array(q{
SELECT inst_id, eq_type, total_req#, total_wait#,
succ_req#, failed_req#, cum_wait_time
FROM v$enqueue_stat
});
foreach (@enqueueresults) {
my ($inst_id, $name, $total_requests, $total_waits,
$succeeded_requests, $failed_requests, $cumul_wait_time) = @{$_};
if ($params{regexp}) {
next if $params{selectname} && $name !~ /$params{selectname}/;
} else {
next if $params{selectname} && lc $params{selectname} ne lc $name;
}
my %thisparams = %params;
$thisparams{name} = $name;
$thisparams{total_requests} = $total_requests;
$thisparams{total_waits} = $total_waits;
$thisparams{succeeded_requests} = $succeeded_requests;
$thisparams{failed_requests} = $failed_requests;
$thisparams{cumul_wait_time} = $cumul_wait_time;
my $enqueue = DBD::Oracle::Server::Instance::Enqueue->new(
%thisparams);
add_enqueue($enqueue);
$num_enqueues++;
}
if (! $num_enqueues) {
$initerrors = 1;
return undef;
}
}
}
}
sub new {
my $class = shift;
my %params = @_;
my $self = {
handle => $params{handle},
name => $params{name},
total_requests => $params{total_requests},
total_waits => $params{total_waits},
succeeded_requests => $params{succeeded_requests},
failed_requests => $params{failed_requests},
cumul_wait_time => $params{cumul_wait_time}, # ! milliseconds
contention => undef,
percent_waited => undef,
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
};
$self->{name} =~ s/^\s+//;
$self->{name} =~ s/\s+$//;
bless $self, $class;
$self->init(%params);
return $self;
}
sub init {
my $self = shift;
my %params = @_;
$self->init_nagios();
if (($params{mode} =~ /server::instance::enqueue::contention/) ||
($params{mode} =~ /server::instance::enqueue::waiting/)) {
$params{differenciator} = lc $self->{name};
$self->valdiff(\%params, qw(total_requests total_waits succeeded_requests
failed_requests cumul_wait_time));
# enqueue contention
$self->{contention} = $self->{delta_total_requests} ?
100 * $self->{delta_total_waits} / $self->{delta_total_requests} : 0;
# enqueue waiting
$self->{percent_waited} = ($self->{delta_cumul_wait_time} /
($self->{delta_timestamp} * 1000)) * 100;
}
}
sub nagios {
my $self = shift;
my %params = @_;
if (! $self->{nagios_level}) {
if ($params{mode} =~ /server::instance::enqueue::contention/) {
$self->add_nagios(
$self->check_thresholds($self->{contention}, "1", "10"),
sprintf "enqueue %s %s: %.2f%% of the requests must wait ",
$self->{name}, $self->longname(), $self->{contention});
$self->add_perfdata(sprintf "'%s_contention'=%.2f%%;%s;%s '%s_requests'=%d '%s_waits'=%d",
$self->{name},
$self->{contention},
$self->{warningrange}, $self->{criticalrange},
$self->{name},
$self->{delta_total_requests},
$self->{name},
$self->{delta_total_waits});
} elsif ($params{mode} =~ /server::instance::enqueue::waiting/) {
$self->add_nagios(
# 1 ms wait in 5 minutes
$self->check_thresholds($self->{percent_waited}, "0.0003333", "0.003333"),
sprintf "enqueue %s %s: waiting %.4f%% of the time",
$self->{name}, $self->longname(), $self->{percent_waited});
$self->add_perfdata(sprintf "'%s_ms_waited'=%d '%s_pct_waited'=%.4f%%;%s;%s",
$self->{name},
$self->{delta_cumul_wait_time},
$self->{name},
$self->{percent_waited},
$self->{warningrange}, $self->{criticalrange});
}
}
}
sub longname {
my $self = shift;
my $abbrev = <
BR, Backup/Restore
CF, Controlfile Transaction
CI, Cross-instance Call Invocation
CU, Bind Enqueue
DF, Datafile
DL, Direct Loader Index Creation
DM, Database Mount
DR, Distributed Recovery Process
DX, Distributed Transaction
FP, File Object
FS, File Set
HW, High-Water Lock
IN, Instance Number
IR, Instance Recovery
IS, Instance State
IV, Library Cache Invalidation
JI, Enqueue used during AJV snapshot refresh
JQ, Job Queue
KK, Redo Log "Kick"
KO, Multiple Object Checkpoint
L[A-P], Library Cache Lock
LS, Log Start or Switch
MM, Mount Definition
MR, Media Recovery
N[A-Z], Library Cache Pin
PE, ALTER SYSTEM SET PARAMETER = VALUE
PF, Password File
PI, Parallel Slaves
PR, Process Startup
PS, Parallel Slave Synchronization
Q[A-Z], Row Cache
RO, Object Reuse
RT, Redo Thread
RW, Row Wait
SC, System Commit Number
SM, SMON
SN, Sequence Number
SQ, Sequence Number Enqueue
SR, Synchronized Replication
SS, Sort Segment
ST, Space Management Transaction
SV, Sequence Number Value
TA, Transaction Recovery
TC, Thread Checkpoint
TE, Extend Table
TM, DML Enqueue
TO, Temporary Table Object Enqueue
TS, Temporary Segment (also TableSpace)
TT, Temporary Table
TX, Transaction
UL, User-defined Locks
UN, User Name
US, Undo Segment, Serialization
WL, Being Written Redo Log
XA, Instance Attribute Lock
XI, Instance Registration Lock
EOEO
my $descriptions = {};
foreach (split(/\n/, $abbrev)) {
my ($short, $descr) = split /,/;
if ($self->{name} =~ /^$short$/) {
$descr =~ s/^\s+//g;
return $descr;
}
}
return "";
}
package DBD::Oracle::Server::Instance::Sysstat;
use strict;
our @ISA = qw(DBD::Oracle::Server::Instance);
{
my @sysstats = ();
my $initerrors = undef;
sub add_sysstat {
push(@sysstats, shift);
}
sub return_sysstats {
return reverse
sort { $a->{name} cmp $b->{name} } @sysstats;
}
sub init_sysstats {
my %params = @_;
my $num_sysstats = 0;
my %longnames = ();
if (($params{mode} =~ /server::instance::sysstat::rate/) ||
($params{mode} =~ /server::instance::sysstat::listsysstats/)) {
my @sysstatresults = $params{handle}->fetchall_array(q{
SELECT statistic#, name, class, value FROM v$sysstat
});
foreach (@sysstatresults) {
my ($number, $name, $class, $value) = @{$_};
if ($params{regexp}) {
next if $params{selectname} && $name !~ /$params{selectname}/;
} else {
next if ($params{selectname} && (
($params{selectname} !~ /^\d+$/ && (lc $params{selectname} ne lc $name)) ||
($params{selectname} =~ /^\d+$/ && ($params{selectname} != $number))));
}
my %thisparams = %params;
$thisparams{name} = $name;
$thisparams{number} = $number;
$thisparams{class} = $class;
$thisparams{value} = $value;
my $sysstat = DBD::Oracle::Server::Instance::Sysstat->new(
%thisparams);
add_sysstat($sysstat);
$num_sysstats++;
}
if (! $num_sysstats) {
$initerrors = 1;
return undef;
}
}
}
}
sub new {
my $class = shift;
my %params = @_;
my $self = {
handle => $params{handle},
name => $params{name},
number => $params{number},
class => $params{class},
value => $params{value},
rate => $params{rate},
count => $params{count},
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
};
#$self->{name} =~ s/^\s+//;
#$self->{name} =~ s/\s+$//;
bless $self, $class;
$self->init(%params);
return $self;
}
sub init {
my $self = shift;
my %params = @_;
$self->init_nagios();
if ($params{mode} =~ /server::instance::sysstat::rate/) {
$params{differenciator} = lc $self->{name};
$self->valdiff(\%params, qw(value));
$self->{rate} = $self->{delta_value} / $self->{delta_timestamp};
}
}
sub nagios {
my $self = shift;
my %params = @_;
if (! $self->{nagios_level}) {
if ($params{mode} =~ /server::instance::sysstat::rate/) {
$self->add_nagios(
$self->check_thresholds($self->{rate}, "10", "100"),
sprintf "%.6f %s/sec", $self->{rate}, $self->{name});
$self->add_perfdata(sprintf "'%s_per_sec'=%.6f;%s;%s",
$self->{name},
$self->{rate},
$self->{warningrange}, $self->{criticalrange});
$self->add_perfdata(sprintf "'%s'=%u",
$self->{name},
$self->{delta_value});
}
}
}
package DBD::Oracle::Server::Instance;
use strict;
our @ISA = qw(DBD::Oracle::Server);
sub new {
my $class = shift;
my %params = @_;
my $self = {
handle => $params{handle},
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
sga => undef,
processes => {},
events => [],
enqueues => [],
};
bless $self, $class;
$self->init(%params);
return $self;
}
sub init {
my $self = shift;
my %params = @_;
$self->init_nagios();
if ($params{mode} =~ /server::instance::sga/) {
$self->{sga} = DBD::Oracle::Server::Instance::SGA->new(%params);
} elsif ($params{mode} =~ /server::instance::pga/) {
$self->{pga} = DBD::Oracle::Server::Instance::PGA->new(%params);
} elsif ($params{mode} =~ /server::instance::sysstat/) {
DBD::Oracle::Server::Instance::Sysstat::init_sysstats(%params);
if (my @sysstats =
DBD::Oracle::Server::Instance::Sysstat::return_sysstats(%params)) {
$self->{sysstats} = \@sysstats;
} else {
$self->add_nagios_critical("unable to aquire sysstats info");
}
} elsif ($params{mode} =~ /server::instance::event/) {
DBD::Oracle::Server::Instance::Event::init_events(%params);
if (my @events =
DBD::Oracle::Server::Instance::Event::return_events(%params)) {
$self->{events} = \@events;
} else {
$self->add_nagios_critical("unable to aquire event info");
}
} elsif ($params{mode} =~ /server::instance::enqueue/) {
DBD::Oracle::Server::Instance::Enqueue::init_enqueues(%params);
if (my @enqueues =
DBD::Oracle::Server::Instance::Enqueue::return_enqueues(%params)) {
$self->{enqueues} = \@enqueues;
} else {
$self->add_nagios_critical("unable to aquire enqueue info");
}
} elsif ($params{mode} =~ /server::instance::connectedusers/) {
$self->{connected_users} = $self->{handle}->fetchrow_array(q{
SELECT ROUND (a.used_process / b.max_process * 100)
FROM (SELECT COUNT (*) used_process FROM v$process) a,
(SELECT VALUE max_process
FROM v$parameter
WHERE name = 'processes') b
WHERE ROWNUM = 1
});
} elsif ($params{mode} =~ /server::instance::blockingsessions/) {
$self->{blocking_sessions} = $self->{handle}->fetchrow_array(q{
SELECT COUNT(*) FROM DBA_BLOCKERS
});
} elsif ($params{mode} =~ /server::instance::sessionnum/) {
$self->{session_num} = $self->{handle}->fetchrow_array(q{
SELECT COUNT(*) FROM v$session
});
} elsif ($params{mode} =~ /server::instance::locktimes/) {
$self->{lock_times} = $self->{handle}->fetchrow_array(q{
SELECT /*+ rule */ MAX (ctime) FROM v$lock WHERE TYPE = 'TX'
});
} elsif ($params{mode} =~ /server::instance::locknumbers/) {
$self->{lock_numbers} = $self->{handle}->fetchrow_array(q{
SELECT /*+ rule */ COUNT (*) FROM v$lock WHERE TYPE = 'TX' AND ctime > 5
});
} elsif ($params{mode} =~ /server::instance::longtransactions/) {
$self->{long_transactions} = $self->{handle}->fetchrow_array(q{
SELECT MAX (last_call_et)
FROM v$session
WHERE status = 'ACTIVE'
AND TYPE = 'USER'
AND username NOT IN ('DBVIEW')
AND sid NOT IN (SELECT sid FROM dba_jobs_running)
AND sid NOT IN (SELECT session_id FROM dba_scheduler_running_jobs)
});
} elsif ($params{mode} =~ /server::instance::usedspaces/) {
$self->{used_spaces} = $self->{handle}->fetchrow_array(q{
select round(sum(bytes)/1024/1024/1024,0) from dba_segments
});
}
}
sub nagios {
my $self = shift;
my %params = @_;
if ($params{mode} =~ /server::instance::sga/) {
$self->{sga}->nagios(%params);
$self->merge_nagios($self->{sga});
} elsif ($params{mode} =~ /server::instance::pga/) {
$self->{pga}->nagios(%params);
$self->merge_nagios($self->{pga});
} elsif ($params{mode} =~ /server::instance::event::listevents/) {
foreach (sort { $a->{name} cmp $b->{name} } @{$self->{events}}) {
printf "%10u%s %s %s\n", $_->{event_id}, $_->{idle} ? '*' : '', $_->{shortname}, $_->{name};
}
$self->add_nagios_ok("have fun");
} elsif ($params{mode} =~ /server::instance::event/) {
foreach (@{$self->{events}}) {
$_->nagios(%params);
$self->merge_nagios($_);
}
if (! $self->{nagios_level} && ! $params{selectname}) {
$self->add_nagios_ok("no wait problems");
}
} elsif ($params{mode} =~ /server::instance::sysstat::listsysstat/) {
foreach (sort { $a->{name} cmp $b->{name} } @{$self->{sysstats}}) {
printf "%10d %s\n", $_->{number}, $_->{name};
}
$self->add_nagios_ok("have fun");
} elsif ($params{mode} =~ /server::instance::sysstat/) {
foreach (@{$self->{sysstats}}) {
$_->nagios(%params);
$self->merge_nagios($_);
}
if (! $self->{nagios_level} && ! $params{selectname}) {
$self->add_nagios_ok("no wait problems");
}
} elsif ($params{mode} =~ /server::instance::enqueue::listenqueues/) {
foreach (sort { $a->{name} cmp $b->{name} } @{$self->{enqueues}}) {
printf "%s\n", $_->{name};
}
$self->add_nagios_ok("have fun");
} elsif ($params{mode} =~ /server::instance::enqueue/) {
foreach (@{$self->{enqueues}}) {
$_->nagios(%params);
$self->merge_nagios($_);
}
if (! $self->{nagios_level} && ! $params{selectname}) {
$self->add_nagios_ok("no enqueue problem");
}
} elsif ($params{mode} =~ /server::instance::connectedusers/) {
$self->add_nagios(
$self->check_thresholds($self->{connected_users}, 70, 90),
sprintf "%d%% processes used",
$self->{connected_users});
$self->add_perfdata(sprintf "processes_used=%d%%;%d%%;%d%%",
$self->{connected_users},
$self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~ /server::instance::blockingsessions/) {
$self->add_nagios(
$self->check_thresholds($self->{blocking_sessions}, 1, 3),
sprintf "%d blocking sessions",
$self->{blocking_sessions});
$self->add_perfdata(sprintf "blocking_sessions=%d;%d;%d",
$self->{blocking_sessions},
$self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~ /server::instance::sessionnum/) {
$self->add_nagios(
$self->check_thresholds($self->{session_num}, 1, 3),
sprintf "%d session num",
$self->{session_num});
$self->add_perfdata(sprintf "session_num=%d;%d;%d",
$self->{session_num},
$self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~ /server::instance::locktimes/) {
$self->add_nagios(
$self->check_thresholds($self->{lock_times}, 7200, 14400),
sprintf "%d second lock times",
$self->{lock_times});
$self->add_perfdata(sprintf "lock_times=%d;%d;%d",
$self->{lock_times},
$self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~ /server::instance::locknumbers/) {
$self->add_nagios(
$self->check_thresholds($self->{lock_numbers}, 20, 30),
sprintf "%d lock numbers",
$self->{lock_numbers});
$self->add_perfdata(sprintf "lock_numbers=%d;%d;%d",
$self->{lock_numbers},
$self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~ /server::instance::longtransactions/) {
$self->add_nagios(
$self->check_thresholds($self->{long_transactions}, 7200, 14400),
sprintf "%d second long transactions",
$self->{long_transactions});
$self->add_perfdata(sprintf "long_transactions=%d;%d;%d",
$self->{long_transactions},
$self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~ /server::instance::usedspaces/) {
$self->add_nagios(
$self->check_thresholds($self->{used_spaces}, 10000, 20000),
sprintf "%d GB space has been used",
$self->{used_spaces});
$self->add_perfdata(sprintf "used_spaces=%dGB;%dGB;%dGB",
$self->{used_spaces},
$self->{warningrange}, $self->{criticalrange});
}
}
package DBD::Oracle::Server::Database::Tablespace::Datafile;
use strict;
use File::Basename;
our @ISA = qw(DBD::Oracle::Server::Database::Tablespace);
{
my @datafiles = ();
my $initerrors = undef;
sub add_datafile {
push(@datafiles, shift);
}
sub return_datafiles {
return reverse
sort { $a->{name} cmp $b->{name} } @datafiles;
}
sub clear_datafiles {
@datafiles = ();
}
sub init_datafiles {
my %params = @_;
my $num_datafiles = 0;
if (($params{mode} =~ /server::database::tablespace::datafile::iotraffic/) ||
($params{mode} =~ /server::database::tablespace::datafile::listdatafiles/)) {
# negative values can occur
# column datafile format a30
my @datafileresults = $params{handle}->fetchall_array(q{
SELECT
name datafile, phyrds reads, phywrts writes
FROM
v$datafile a, v$filestat b
WHERE
a.file# = b.file#
UNION
SELECT
name datafile, phyrds reads, phywrts writes
FROM
v$tempfile a, v$tempstat b
WHERE
a.file# = b.file#
});
if (DBD::Oracle::Server::return_first_server()->windows_server()) {
fileparse_set_fstype("MSWin32");
}
foreach (@datafileresults) {
my ($name, $phyrds, $phywrts) = @{$_};
if ($params{regexp}) {
next if $params{selectname} &&
(($name !~ /$params{selectname}/) &&
(basename($name) !~ /$params{selectname}/));
} else {
next if $params{selectname} &&
((lc $params{selectname} ne lc $name) &&
(lc $params{selectname} ne lc basename($name)));
}
my %thisparams = %params;
$thisparams{path} = $name;
$thisparams{name} = basename($name);
$thisparams{phyrds} = $phyrds;
$thisparams{phywrts} = $phywrts;
my $datafile =
DBD::Oracle::Server::Database::Tablespace::Datafile->new(
%thisparams);
add_datafile($datafile);
$num_datafiles++;
}
} elsif ($params{mode} =~ /server::database::tablespace::iobalance/) {
my $sql = q{
-- SELECT REGEXP_REPLACE(file_name,'^.*.\/.*.\/', '') file_name,
SELECT file_name,
SUM(phyrds), SUM(phywrts)
FROM dba_temp_files, v$filestat
WHERE tablespace_name = UPPER(?)
AND file_id=file# GROUP BY tablespace_name, file_name
UNION
-- SELECT REGEXP_REPLACE(file_name,'^.*.\/.*.\/', '') file_name,
SELECT file_name,
SUM(phyrds), SUM(phywrts)
FROM dba_data_files, v$filestat
WHERE tablespace_name = UPPER(?)
AND file_id=file# GROUP BY tablespace_name, file_name };
if (! DBD::Oracle::Server::return_first_server()->version_is_minimum("9.2.0.3")) {
# bug 2436600
$sql = q{
-- SELECT REGEXP_REPLACE(file_name,'^.*.\/.*.\/', '') file_name,
SELECT file_name,
SUM(phyrds), SUM(phywrts)
FROM dba_data_files, v$filestat
WHERE tablespace_name = UPPER(?)
AND file_id=file# GROUP BY tablespace_name, file_name };
}
my @datafileresults = $params{handle}->fetchall_array($sql, $params{selectname}, $params{selectname});
if (DBD::Oracle::Server::return_first_server()->windows_server()) {
fileparse_set_fstype("MSWin32");
}
foreach (@datafileresults) {
my ($name, $phyrds, $phywrts) = @{$_};
my %thisparams = %params;
$thisparams{path} = $name;
$thisparams{name} = basename($name);
$thisparams{phyrds} = $phyrds;
$thisparams{phywrts} = $phywrts;
my $datafile =
DBD::Oracle::Server::Database::Tablespace::Datafile->new(
%thisparams);
add_datafile($datafile);
$num_datafiles++;
}
if (! $num_datafiles) {
$initerrors = 1;
return undef;
}
}
}
}
sub new {
my $class = shift;
my %params = @_;
my $self = {
handle => $params{handle},
path => $params{path},
name => $params{name},
phyrds => $params{phyrds},
phywrts => $params{phywrts},
io_total => undef,
io_total_per_sec => undef,
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
};
bless $self, $class;
$self->init(%params);
return $self;
}
sub init {
my $self = shift;
my %params = @_;
$self->init_nagios();
if ($params{mode} =~ /server::database::tablespace::iobalance/) {
if (! defined $self->{phyrds}) {
$self->add_nagios_critical(sprintf "unable to read datafile io %s", $@);
} else {
$params{differenciator} = $self->{path};
$self->valdiff(\%params, qw(phyrds phywrts));
$self->{io_total} = $self->{delta_phyrds} + $self->{delta_phywrts};
}
} elsif ($params{mode} =~ /server::database::tablespace::datafile::iotraffic/) {
if (! defined $self->{phyrds}) {
$self->add_nagios_critical(sprintf "unable to read datafile io %s", $@);
} else {
$params{differenciator} = $self->{path};
$self->valdiff(\%params, qw(phyrds phywrts));
$self->{io_total_per_sec} = ($self->{delta_phyrds} + $self->{delta_phywrts}) /
$self->{delta_timestamp};
}
}
}
sub nagios {
my $self = shift;
my %params = @_;
if (! $self->{nagios_level}) {
if ($params{mode} =~ /server::database::tablespace::datafile::iotraffic/) {
$self->add_nagios(
$self->check_thresholds($self->{io_total_per_sec}, "1000", "5000"),
sprintf ("%s: %.2f IO Operations per Second",
$self->{name}, $self->{io_total_per_sec}));
$self->add_perfdata(sprintf "'dbf_%s_io_total_per_sec'=%.2f;%d;%d",
$self->{name}, $self->{io_total_per_sec},
$self->{warningrange}, $self->{criticalrange});
}
}
}
package DBD::Oracle::Server::Database::Tablespace::Segment;
use strict;
our @ISA = qw(DBD::Oracle::Server::Database::Tablespace);
{
my @segments = ();
my $initerrors = undef;
sub add_segment {
push(@segments, shift);
}
sub return_segments {
return reverse
sort { $a->{name} cmp $b->{name} } @segments;
}
sub clear_segments {
@segments = ();
}
sub init_segments {
my %params = @_;
my $num_segments = 0;
if (($params{mode} =~
/server::database::tablespace::segment::top10logicalreads/) ||
($params{mode} =~
/server::database::tablespace::segment::top10physicalreads/) ||
($params{mode} =~
/server::database::tablespace::segment::top10bufferbusywaits/) ||
($params{mode} =~
/server::database::tablespace::segment::top10rowlockwaits/)) {
my %thisparams = %params;
$thisparams{name} = "dummy_segment";
my $segment = DBD::Oracle::Server::Database::Tablespace::Segment->new(
%thisparams);
add_segment($segment);
$num_segments++;
} elsif ($params{mode} =~
/server::database::tablespace::segment::extendspace/) {
my @tablespaceresult = $params{handle}->fetchall_array(q{
SELECT /*+ RULE */
-- tablespace, segment, extent
-- aber dadurch, dass nur das letzte extent selektiert wird
-- werden praktisch nur tablespace und segmente ausgegeben
b.tablespace_name "Tablespace",
b.segment_type "Type",
SUBSTR(ext.owner||'.'||ext.segment_name,1,50) "Object Name",
DECODE(freespace.extent_management,
'DICTIONARY', DECODE(b.extents,
1, b.next_extent, ext.bytes * (1 + b.pct_increase / 100)),
'LOCAL', DECODE(freespace.allocation_type,
'UNIFORM', freespace.initial_extent,
'SYSTEM', ext.bytes)
) "Required Extent",
freespace.largest "MaxAvail"
FROM
-- dba_segments b,
-- dba_extents ext,
(
SELECT
owner, segment_type, segment_name, extents, pct_increase,
next_extent, tablespace_name
FROM
dba_segments
WHERE
tablespace_name = ?
) b,
(
SELECT
owner, segment_type, segment_name, extent_id, bytes,
tablespace_name
FROM
dba_extents
WHERE
tablespace_name = ?
) ext,
(
-- dictionary/local, uniform/system, initial, next
-- und der groesste freie extent pro tablespace
SELECT
b.tablespace_name,
b.extent_management,
b.allocation_type,
b.initial_extent,
b.next_extent,
max(a.bytes) largest
FROM
dba_free_space a,
dba_tablespaces b
WHERE
b.tablespace_name = a.tablespace_name
AND
b.status = 'ONLINE'
GROUP BY
b.tablespace_name,
b.extent_management,
b.allocation_type,
b.initial_extent,
b.next_extent
) freespace
WHERE
b.owner = ext.owner
AND
b.segment_type = ext.segment_type
AND
b.segment_name = ext.segment_name
AND
b.tablespace_name = ext.tablespace_name
AND
-- so landet nur das jeweils letzte extent im ergebnis
(b.extents - 1) = ext.extent_id
AND
b.tablespace_name = freespace.tablespace_name
AND
freespace.tablespace_name = ?
ORDER BY
b.tablespace_name,
b.segment_type,
b.segment_name
}, $params{tablespace}, $params{tablespace}, $params{tablespace});
foreach (@tablespaceresult) {
my ($tablespace_name, $segment_type, $object_name,
$required_for_next_extent, $largest_free) = @{$_};
my %thisparams = %params;
$thisparams{name} = $object_name;
$thisparams{segment_type} = $segment_type;
$thisparams{required_for_next_extent} = $required_for_next_extent;
$thisparams{largest_free} = $largest_free;
my $segment = DBD::Oracle::Server::Database::Tablespace::Segment->new(
%thisparams);
add_segment($segment);
$num_segments++;
}
}
if (! $num_segments) {
$initerrors = 1;
return undef;
}
}
}
sub new {
my $class = shift;
my %params = @_;
my $self = {
handle => $params{handle},
name => $params{name},
segment_type => $params{segment_type},
required_for_next_extent => $params{required_for_next_extent},
largest_free => $params{largest_free},
num_users_among_top10logicalreads => undef,
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
};
bless $self, $class;
$self->init(%params);
return $self;
}
sub init {
my $self = shift;
my %params = @_;
$self->init_nagios();
if (($params{mode} =~
/server::database::tablespace::segment::top10logicalreads/) ||
($params{mode} =~
/server::database::tablespace::segment::top10physicalreads/) ||
($params{mode} =~
/server::database::tablespace::segment::top10bufferbusywaits/) ||
($params{mode} =~
/server::database::tablespace::segment::top10rowlockwaits/)) {
my $mode = (split(/::/, $params{mode}))[4];
## -- SELECT owner, object_name, object_type, value, statistic_name
my $sql = q{
SELECT COUNT(*)
FROM (select DO.owner, DO.object_name, DO.object_type, SS.value,
SS.statistic_name, row_number () over (order by value desc) RN
FROM dba_objects DO, v$segstat SS
WHERE DO.object_id = SS.obj#
AND statistic_name = ?)
WHERE RN <= 10
AND owner not in
('CTXSYS', 'DBSNMP', 'MDDATA', 'MDSYS', 'DMSYS', 'OLAPSYS',
'ORDPLUGINS', 'ORDSYS', 'OUTLN', 'SI_INFORMTN_SCHEMA',
'SYS', 'SYSMAN', 'SYSTEM')
};
$sql = q{
select DO.owner, DO.object_name, DO.object_type, SS.value,
SS.statistic_name
FROM dba_objects DO, v$segstat SS
WHERE DO.object_id = SS.obj#
AND statistic_name = ?
};
my $statname = {
top10logicalreads => "logical reads",
top10physicalreads => "physical reads",
top10bufferbusywaits => "buffer busy waits",
top10rowlockwaits => "row lock waits",
}->{$mode};
#$self->{"num_users_among_".$mode} =
# $self->{handle}->fetchrow_array($sql, $statname);
# faster version
# fetch everything
my @sortedsessions = reverse sort { $a->[3] <=> $b->[3] } $self->{handle}->fetchall_array($sql, $statname);
if (scalar(@sortedsessions) > 10) {
@sortedsessions = (@sortedsessions)[0..9];
}
my @usersessions = map { $_->[0] !~ /^(CTXSYS|DBSNMP|MDDATA|MDSYS|DMSYS|OLAPSYS|ORDPLUGINS|ORDSYS|OUTLN|SI_INFORMTN_SCHEMA|SYS|SYSMAN|SYSTEM)$/ ? $_ : () } @sortedsessions;
$self->{"num_users_among_".$mode} = scalar(@usersessions);
if (scalar(@sortedsessions) == 0) {
#if (! defined $self->{"num_users_among_".$mode}) {
$self->add_nagios_critical(sprintf "unable to read top10: %s", $@);
}
}
}
sub nagios {
my $self = shift;
my %params = @_;
if (! $self->{nagios_level}) {
if (($params{mode} =~
/server::database::tablespace::segment::top10logicalreads/) ||
($params{mode} =~
/server::database::tablespace::segment::top10physicalreads/) ||
($params{mode} =~
/server::database::tablespace::segment::top10bufferbusywaits/) ||
($params{mode} =~
/server::database::tablespace::segment::top10rowlockwaits/)) {
my $mode = (split(/::/, $params{mode}))[4];
my $statname = {
top10logicalreads => "logical reads",
top10physicalreads => "physical reads",
top10bufferbusywaits => "buffer busy waits",
top10rowlockwaits => "row lock waits",
}->{$mode};
$self->add_nagios(
$self->check_thresholds(
$self->{"num_users_among_".$mode}, "1", "9"),
sprintf "%d user processes among the top10 %s",
$self->{"num_users_among_".$mode}, $statname);
$statname =~ s/\s/_/g;
$self->add_perfdata(sprintf "users_among_top10_%s=%d;%d;%d",
$statname, $self->{"num_users_among_".$mode},
$self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~
/server::database::tablespace::segment::extendspace/) {
if ($self->{required_for_next_extent} > $self->{largest_free}) {
$self->add_nagios_critical(
sprintf "segment %s cannot extend", $self->{name});
}
}
}
}
package DBD::Oracle::Server::Database::Tablespace;
use strict;
our @ISA = qw(DBD::Oracle::Server::Database);
{
my @tablespaces = ();
my $initerrors = undef;
sub add_tablespace {
push(@tablespaces, shift);
}
sub return_tablespaces {
return reverse
sort { $a->{name} cmp $b->{name} } @tablespaces;
}
sub init_tablespaces {
my %params = @_;
my $num_tablespaces = 0;
if (($params{mode} =~ /server::database::tablespace::usage/) ||
($params{mode} =~ /server::database::tablespace::free/) ||
($params{mode} =~ /server::database::tablespace::remainingfreetime/) ||
($params{mode} =~ /server::database::tablespace::listtablespaces/)) {
my @tablespaceresult = ();
if (DBD::Oracle::Server::return_first_server()->version_is_minimum("9.x")) {
@tablespaceresult = $params{handle}->fetchall_array(q{
SELECT
a.tablespace_name "Tablespace",
b.status "Status",
b.contents "Type",
b.extent_management "Extent Mgmt",
a.bytes bytes,
a.maxbytes bytes_max,
c.bytes_free bytes_free
FROM
(
-- belegter und maximal verfuegbarer platz pro datafile
-- nach tablespacenamen zusammengefasst
-- => bytes
-- => maxbytes
SELECT
a.tablespace_name,
SUM(a.bytes) bytes,
SUM(DECODE(a.autoextensible, 'YES', a.maxbytes, 'NO', a.bytes)) maxbytes
FROM
dba_data_files a
GROUP BY
tablespace_name
) a,
sys.dba_tablespaces b,
(
-- freier platz pro tablespace
-- => bytes_free
SELECT
a.tablespace_name,
SUM(a.bytes) bytes_free
FROM
dba_free_space a
GROUP BY
tablespace_name
) c
WHERE
a.tablespace_name = c.tablespace_name (+)
AND a.tablespace_name = b.tablespace_name
UNION ALL
SELECT
a.tablespace_name "Tablespace",
b.status "Status",
b.contents "Type",
b.extent_management "Extent Mgmt",
sum(a.bytes_free + a.bytes_used) bytes, -- allocated
SUM(DECODE(d.autoextensible, 'YES', d.maxbytes, 'NO', d.bytes)) bytes_max,
SUM(a.bytes_free + a.bytes_used - NVL(c.bytes_used, 0)) bytes_free
FROM
sys.v_$TEMP_SPACE_HEADER a,
sys.dba_tablespaces b,
sys.v_$Temp_extent_pool c,
dba_temp_files d
WHERE
c.file_id(+) = a.file_id
and c.tablespace_name(+) = a.tablespace_name
and d.file_id = a.file_id
and d.tablespace_name = a.tablespace_name
and b.tablespace_name = a.tablespace_name
GROUP BY
a.tablespace_name,
b.status,
b.contents,
b.extent_management,
d.maxbytes
ORDER BY 1
});
} else {
@tablespaceresult = $params{handle}->fetchall_array(q{
SELECT
a.tablespace_name "Tablespace",
b.status "Status",
b.contents "Type",
b.extent_management "Extent Mgmt",
a.bytes bytes,
a.maxbytes bytes_max,
c.bytes_free bytes_free
FROM
(
-- belegter und maximal verfuegbarer platz pro datafile
-- nach tablespacenamen zusammengefasst
-- => bytes
-- => maxbytes
SELECT
a.tablespace_name,
SUM(a.bytes) bytes,
SUM(DECODE(a.autoextensible, 'YES', a.maxbytes, 'NO', a.bytes)) maxbytes
FROM
dba_data_files a
GROUP BY
tablespace_name
) a,
sys.dba_tablespaces b,
(
-- freier platz pro tablespace
-- => bytes_free
SELECT
a.tablespace_name,
SUM(a.bytes) bytes_free
FROM
dba_free_space a
GROUP BY
tablespace_name
) c
WHERE
a.tablespace_name = c.tablespace_name (+)
AND a.tablespace_name = b.tablespace_name
AND (100-(c.bytes_free/a.bytes)*100)>79
UNION ALL
SELECT
a.tablespace_name "Tablespace",
b.status "Status",
b.contents "Type",
b.extent_management "Extent Mgmt",
sum(a.bytes_free + a.bytes_used) bytes, -- allocated
d.maxbytes bytes_max,
SUM(a.bytes_free + a.bytes_used - NVL(c.bytes_used, 0)) bytes_free
FROM
sys.v_$TEMP_SPACE_HEADER a,
sys.dba_tablespaces b,
sys.v_$Temp_extent_pool c,
dba_temp_files d
WHERE
c.file_id(+) = a.file_id
and c.tablespace_name(+) = a.tablespace_name
and d.file_id = a.file_id
and d.tablespace_name = a.tablespace_name
and b.tablespace_name = a.tablespace_name
GROUP BY
a.tablespace_name,
b.status,
b.contents,
b.extent_management,
d.maxbytes
HAVING sum(a.bytes_used)/sum(a.bytes_used+a.bytes_free)>=79
ORDER BY
1
});
}
foreach (@tablespaceresult) {
my ($name, $status, $type, $extentmgmt, $bytes, $bytes_max, $bytes_free) = @{$_};
if ($params{regexp}) {
next if $params{selectname} && $name !~ /$params{selectname}/;
} else {
next if $params{selectname} && lc $params{selectname} ne lc $name;
}
my %thisparams = %params;
$thisparams{name} = $name;
$thisparams{bytes} = $bytes;
$thisparams{bytes_max} = $bytes_max;
$thisparams{bytes_free} = $bytes_free;
$thisparams{status} = lc $status;
$thisparams{type} = lc $type;
$thisparams{extent_management} = lc $extentmgmt;
my $tablespace = DBD::Oracle::Server::Database::Tablespace->new(
%thisparams);
add_tablespace($tablespace);
$num_tablespaces++;
}
if (! $num_tablespaces) {
$initerrors = 1;
return undef;
}
} elsif ($params{mode} =~ /server::database::tablespace::fragmentation/) {
my @tablespaceresult = $params{handle}->fetchall_array(q{
SELECT
tablespace_name,
COUNT(*) free_chunks,
DECODE(
ROUND((max(bytes) / 1024000),2),
NULL,0,
ROUND((MAX(bytes) / 1024000),2)) largest_chunk,
NVL(ROUND(SQRT(MAX(blocks)/SUM(blocks))*(100/SQRT(SQRT(COUNT(blocks)) )),2),
0) fragmentation_index
FROM
sys.dba_free_space
GROUP BY
tablespace_name
ORDER BY
2 DESC, 1
});
foreach (@tablespaceresult) {
my ($name, $free_chunks, $largest_chunk, $fragmentation_index) = @{$_};
if ($params{regexp}) {
next if $params{selectname} && $name !~ /$params{selectname}/;
} else {
next if $params{selectname} && lc $params{selectname} ne lc $name;
}
my %thisparams = %params;
$thisparams{name} = $name;
$thisparams{fsfi} = $fragmentation_index;
my $tablespace = DBD::Oracle::Server::Database::Tablespace->new(
%thisparams);
add_tablespace($tablespace);
$num_tablespaces++;
}
if (! $num_tablespaces) {
$initerrors = 1;
return undef;
}
} elsif ($params{mode} =~ /server::database::tablespace::segment::top10/) {
my %thisparams = %params;
$thisparams{name} = "dummy_segment";
$thisparams{segments} = [];
my $tablespace = DBD::Oracle::Server::Database::Tablespace->new(
%thisparams);
add_tablespace($tablespace);
} elsif ($params{mode} =~
/server::database::tablespace::segment::extendspace/) {
my @tablespaceresult = $params{handle}->fetchall_array(q{
SELECT
tablespace_name, extent_management, allocation_type
FROM
dba_tablespaces
});
foreach (@tablespaceresult) {
my ($name, $extent_management, $allocation_type) = @{$_};
if ($params{regexp}) {
next if $params{selectname} && $name !~ /$params{selectname}/;
} else {
next if $params{selectname} && lc $params{selectname} ne lc $name;
}
my %thisparams = %params;
$thisparams{name} = $name;
$thisparams{extent_management} = $extent_management;
$thisparams{allocation_type} = $allocation_type;
my $tablespace = DBD::Oracle::Server::Database::Tablespace->new(
%thisparams);
add_tablespace($tablespace);
$num_tablespaces++;
}
if (! $num_tablespaces) {
$initerrors = 1;
return undef;
}
} elsif ($params{mode} =~ /server::database::tablespace::datafile/) {
my %thisparams = %params;
$thisparams{name} = "dummy_for_datafiles";
$thisparams{datafiles} = [];
my $tablespace = DBD::Oracle::Server::Database::Tablespace->new(
%thisparams);
add_tablespace($tablespace);
} elsif ($params{mode} =~ /server::database::tablespace::iobalance/) {
my @tablespaceresult = $params{handle}->fetchall_array(q{
SELECT tablespace_name FROM dba_tablespaces
});
foreach (@tablespaceresult) {
my ($name) = @{$_};
if ($params{regexp}) {
next if $params{selectname} && $name !~ /$params{selectname}/;
} else {
next if $params{selectname} && lc $params{selectname} ne lc $name;
}
my %thisparams = %params;
$thisparams{name} = $name;
my $tablespace = DBD::Oracle::Server::Database::Tablespace->new(
%thisparams);
add_tablespace($tablespace);
$num_tablespaces++;
}
if (! $num_tablespaces) {
$initerrors = 1;
return undef;
}
}
}
}
sub new {
my $class = shift;
my %params = @_;
my $self = {
handle => $params{handle},
name => $params{name},
bytes => $params{bytes},
bytes_max => $params{bytes_max},
bytes_free => $params{bytes_free} || 0,
extent_management => $params{extent_management},
type => $params{type},
status => $params{status},
fsfi => $params{fsfi},
segments => [],
datafiles => [],
io_total => 0,
usage_history => [],
extent_management => $params{extent_management},
allocation_type => $params{allocation_type},
largest_free_extent => $params{largest_free_extent},
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
};
bless $self, $class;
$self->init(%params);
return $self;
}
sub init {
my $self = shift;
my %params = @_;
$self->init_nagios();
if ($params{mode} =~ /server::database::tablespace::(usage|free)/) {
if (! defined $self->{bytes_max}) {
$self->{bytes} = 0;
$self->{bytes_max} = 0;
$self->{bytes_free} = 0;
$self->{percent_used} = 0;
$self->{real_bytes_max} = $self->{bytes};
$self->{real_bytes_free} = $self->{bytes_free};
$self->{percent_as_bar} = '____________________';
} else {
# (total - free) / total * 100 = % used
# (used + free - free) / ( used + free)
if ($self->{bytes_max} == 0) {
$self->{percent_used} =
($self->{bytes} - $self->{bytes_free}) / $self->{bytes} * 100;
$self->{real_bytes_max} = $self->{bytes};
$self->{real_bytes_free} = $self->{bytes_free};
} elsif ($self->{bytes_max} > $self->{bytes}) {
$self->{percent_used} =
($self->{bytes} - $self->{bytes_free}) / $self->{bytes_max} * 100;
$self->{real_bytes_max} = $self->{bytes_max};
$self->{real_bytes_free} = $self->{bytes_free} + ($self->{bytes_max} - $self->{bytes});
} else {
# alter tablespace USERS add datafile 'users02.dbf'
# size 5M autoextend on next 200K maxsize 6M;
# bytes = 5M, maxbytes = 6M
# ..... data arriving...until ORA-01652: unable to extend temp segment
# bytes = 6M, maxbytes = 6M
# alter database datafile 5 resize 8M;
# bytes = 8M, maxbytes = 6M
$self->{percent_used} =
($self->{bytes} - $self->{bytes_free}) / $self->{bytes} * 100;
$self->{real_bytes_max} = $self->{bytes};
$self->{real_bytes_free} = $self->{bytes_free};
}
}
$self->{percent_free} = 100 - $self->{percent_used};
my $tlen = 20;
my $len = int((($params{mode} =~ /server::database::tablespace::usage/) ?
$self->{percent_used} : $self->{percent_free} / 100 * $tlen) + 0.5);
$self->{percent_as_bar} = '=' x $len . '_' x ($tlen - $len);
} elsif ($params{mode} =~ /server::database::tablespace::fragmentation/) {
} elsif ($params{mode} =~ /server::database::tablespace::segment::top10/) {
DBD::Oracle::Server::Database::Tablespace::Segment::init_segments(%params);
if (my @segments =
DBD::Oracle::Server::Database::Tablespace::Segment::return_segments()) {
$self->{segments} = \@segments;
} else {
$self->add_nagios_critical("unable to aquire segment info");
}
} elsif ($params{mode} =~ /server::database::tablespace::datafile/) {
DBD::Oracle::Server::Database::Tablespace::Datafile::init_datafiles(%params);
if (my @datafiles =
DBD::Oracle::Server::Database::Tablespace::Datafile::return_datafiles()) {
$self->{datafiles} = \@datafiles;
} else {
$self->add_nagios_critical("unable to aquire datafile info");
}
} elsif ($params{mode} =~ /server::database::tablespace::iobalance/) {
$params{tablespace} = $self->{name};
DBD::Oracle::Server::Database::Tablespace::Datafile::init_datafiles(%params);
if (my @datafiles =
DBD::Oracle::Server::Database::Tablespace::Datafile::return_datafiles()) {
$self->{datafiles} = \@datafiles;
map { $self->{io_total} += $_->{io_total} } @datafiles;
DBD::Oracle::Server::Database::Tablespace::Datafile::clear_datafiles();
} else {
$self->add_nagios_critical("unable to aquire datafile info");
}
} elsif ($params{mode} =~ /server::database::tablespace::segment::extendspace/) {
$params{tablespace} = $self->{name};
DBD::Oracle::Server::Database::Tablespace::Segment::init_segments(%params);
my @segments =
DBD::Oracle::Server::Database::Tablespace::Segment::return_segments();
$self->{segments} = \@segments;
DBD::Oracle::Server::Database::Tablespace::Segment::clear_segments();
} elsif ($params{mode} =~ /server::database::tablespace::remainingfreetime/) {
# load historical data
# calculate slope, intercept (go back periods * interval)
# calculate remaining time
$self->{percent_used} = $self->{bytes_max} == 0 ?
($self->{bytes} - $self->{bytes_free}) / $self->{bytes} * 100 :
($self->{bytes} - $self->{bytes_free}) / $self->{bytes_max} * 100;
$self->{usage_history} = $self->load_state( %params ) || [];
my $now = time;
if (scalar(@{$self->{usage_history}})) {
$self->trace(sprintf "loaded %d data sets from %s - %s",
scalar(@{$self->{usage_history}}),
scalar localtime((@{$self->{usage_history}})[0]->[0]),
scalar localtime($now));
# only data sets with valid usage. only newer than 91 days
$self->{usage_history} =
[ grep { defined $_->[1] && ($now - $_->[0]) < 7862400 } @{$self->{usage_history}} ];
$self->trace(sprintf "trimmed to %d data sets from %s - %s",
scalar(@{$self->{usage_history}}),
scalar localtime((@{$self->{usage_history}})[0]->[0]),
scalar localtime($now));
} else {
$self->trace(sprintf "no historical data found");
}
push(@{$self->{usage_history}}, [ time, $self->{percent_used} ]);
$params{save} = $self->{usage_history};
$self->save_state(%params);
}
}
sub nagios {
my $self = shift;
my %params = @_;
if (! $self->{nagios_level}) {
if ($params{mode} =~ /server::database::tablespace::usage/) {
if (! $self->{bytes_max}) {
$self->check_thresholds($self->{percent_used}, "90", "98");
if ($self->{status} eq 'offline') {
$self->add_nagios_warning(
sprintf("tbs %s is offline", $self->{name})
);
} else {
$self->add_nagios_critical(
sprintf("tbs %s has has a problem, maybe needs recovery?", $self->{name})
);
}
} else {
#if ($self->check_thresholds($self->{percent_used}, "90", "98") != '0'){
$self->add_nagios(
# 'tbs_system_usage_pct'=99.01%;90;98 percent used, warn, crit
# 'tbs_system_usage'=693MB;630;686;0;700 used, warn, crit, 0, max=total
$self->check_thresholds($self->{percent_used}, "90", "98"),
$params{eyecandy} ?
sprintf("[%s] %s", $self->{percent_as_bar}, $self->{name}) :
sprintf("tbs %s usage is %.2f%%",
$self->{name}, $self->{percent_used})
);
#}
}
$self->add_perfdata(sprintf "\'%s\'=%dMB",
lc $self->{name},
($self->{bytes} - $self->{bytes_free}) / 1048576);
} elsif ($params{mode} =~ /server::database::tablespace::fragmentation/) {
$self->add_nagios(
$self->check_thresholds($self->{fsfi}, "30:", "20:"),
sprintf "tbs %s fsfi is %.2f", $self->{name}, $self->{fsfi});
$self->add_perfdata(sprintf "\'tbs_%s_fsfi\'=%.2f;%s;%s;0;100",
lc $self->{name},
$self->{fsfi},
$self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~ /server::database::tablespace::free/) {
# ->percent_free
# ->real_bytes_max
#
# ausgabe
# perfdata tbs_
# perfdata tbs_
# perfdata tbs_
#
# umrechnen der thresholds
# ()/%
# MB
# GB
# KB
if (($self->{warningrange} && $self->{warningrange} !~ /^\d+:/) ||
($self->{criticalrange} && $self->{criticalrange} !~ /^\d+:/)) {
$self->add_nagios_unknown("you want an alert if free space is _above_ a threshold????");
return;
}
if (! $params{units}) {
$params{units} = "%";
}
$self->{warning_bytes} = 0;
$self->{critical_bytes} = 0;
if ($params{units} eq "%") {
if (! $self->{bytes_max}) {
$self->check_thresholds($self->{percent_used}, "5:", "2:");
if ($self->{status} eq 'offline') {
$self->add_nagios_warning(
sprintf("tbs %s is offline", $self->{name})
);
} else {
$self->add_nagios_critical(
sprintf("tbs %s has has a problem, maybe needs recovery?", $self->{name})
);
}
} else {
$self->add_nagios(
$self->check_thresholds($self->{percent_free}, "5:", "2:"),
sprintf("tbs %s has %.2f%% free space left",
$self->{name}, $self->{percent_free})
);
}
$self->{warningrange} =~ s/://g;
$self->{criticalrange} =~ s/://g;
$self->add_perfdata(sprintf "\'tbs_%s_free_pct\'=%.2f%%;%d:;%d:",
lc $self->{name},
$self->{percent_free},
$self->{warningrange}, $self->{criticalrange});
$self->add_perfdata(sprintf "\'tbs_%s_free\'=%dMB;%.2f:;%.2f:;0;%.2f",
lc $self->{name},
$self->{real_bytes_free} / 1048576,
$self->{warningrange} * $self->{bytes_max} / 100 / 1048576,
$self->{criticalrange} * $self->{bytes_max} / 100 / 1048576,
$self->{real_bytes_max} / 1048576);
} else {
my $factor = 1024 * 1024; # default MB
if ($params{units} eq "GB") {
$factor = 1024 * 1024 * 1024;
} elsif ($params{units} eq "MB") {
$factor = 1024 * 1024;
} elsif ($params{units} eq "KB") {
$factor = 1024;
}
$self->{warningrange} ||= "5:";
$self->{criticalrange} ||= "2:";
my $saved_warningrange = $self->{warningrange};
my $saved_criticalrange = $self->{criticalrange};
# : entfernen weil gerechnet werden muss
$self->{warningrange} =~ s/://g;
$self->{criticalrange} =~ s/://g;
$self->{warningrange} = $self->{warningrange} ?
$self->{warningrange} * $factor : 5 * $factor;
$self->{criticalrange} = $self->{criticalrange} ?
$self->{criticalrange} * $factor : 2 * $factor;
if (! $self->{bytes_max}) {
$self->{percent_warning} = 0;
$self->{percent_critical} = 0;
$self->{warningrange} .= ':';
$self->{criticalrange} .= ':';
$self->check_thresholds($self->{real_bytes_free}, "5242880:", "1048576:");
if ($self->{status} eq 'offline') {
$self->add_nagios_warning(
sprintf("tbs %s is offline", $self->{name})
);
} else {
$self->add_nagios_critical(
sprintf("tbs %s has a problem, maybe needs recovery?", $self->{name})
);
}
} else {
$self->{percent_warning} = 100 * $self->{warningrange} / $self->{real_bytes_max};
$self->{percent_critical} = 100 * $self->{criticalrange} / $self->{real_bytes_max};
$self->{warningrange} .= ':';
$self->{criticalrange} .= ':';
$self->add_nagios(
$self->check_thresholds($self->{real_bytes_free}, "5242880:", "1048576:"),
sprintf("tbs %s has %.2f%s free space left", $self->{name},
$self->{real_bytes_free} / $factor, $params{units})
);
}
$self->{warningrange} = $saved_warningrange;
$self->{criticalrange} = $saved_criticalrange;
$self->{warningrange} =~ s/://g;
$self->{criticalrange} =~ s/://g;
$self->add_perfdata(sprintf "\'tbs_%s_free_pct\'=%.2f%%;%.2f:;%.2f:",
lc $self->{name},
$self->{percent_free}, $self->{percent_warning},
$self->{percent_critical});
$self->add_perfdata(sprintf "\'tbs_%s_free\'=%.2f%s;%.2f:;%.2f:;0;%.2f",
lc $self->{name},
$self->{real_bytes_free} / $factor, $params{units},
$self->{warningrange},
$self->{criticalrange},
$self->{real_bytes_max} / $factor);
}
} elsif ($params{mode} =~ /server::database::tablespace::fragmentation/) {
$self->add_nagios(
$self->check_thresholds($self->{fsfi}, "30:", "20:"),
sprintf "tbs %s fsfi is %.2f", $self->{name}, $self->{fsfi});
$self->add_perfdata(sprintf "\'tbs_%s_fsfi\'=%.2f;%s;%s;0;100",
lc $self->{name},
$self->{fsfi},
$self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~ /server::database::tablespace::segment::top10/) {
foreach (@{$self->{segments}}) {
$_->nagios(%params);
$self->merge_nagios($_);
}
} elsif ($params{mode} =~ /server::database::tablespace::datafile::listdatafiles/) {
foreach (sort { $a->{name} cmp $b->{name}; } @{$self->{datafiles}}) {
printf "%s\n", $_->{name};
}
$self->add_nagios_ok("have fun");
} elsif ($params{mode} =~ /server::database::tablespace::datafile/) {
foreach (@{$self->{datafiles}}) {
$_->nagios(%params);
$self->merge_nagios($_);
}
} elsif ($params{mode} =~ /server::database::tablespace::iobalance/) {
my $cv = 0;
if (scalar(@{$self->{datafiles}}) == 0) {
$self->add_nagios($self->check_thresholds($cv, 50, 100),
sprintf "%s has no datafiles", $self->{name});
} elsif (scalar(@{$self->{datafiles}}) == 1) {
$self->add_nagios($self->check_thresholds($cv, 50, 100),
sprintf "%s has just 1 datafile", $self->{name});
} elsif ($self->{io_total} == 0) {
# nix los
$self->check_thresholds(0, 50, 100);
$self->add_nagios_ok(sprintf "%s datafiles io is well balanced",
$self->{name});
} else {
my @unbalanced_datafiles = ();
my $worstfactor = 0;
my $level = $ERRORS{OK};
#
#
# arithmetisches mittel der stichprobe "x quer"
my $averagetotal = $self->{io_total} / scalar(@{$self->{datafiles}});
# standardabweichung
my $sum = 0;
foreach (@{$self->{datafiles}}) {
$sum += ($_->{io_total} - $averagetotal) ** 2;
}
my $sx = sqrt ($sum / (scalar(@{$self->{datafiles}}) - 1));
# relative standardabweichung (%RSD)
$cv = $sx / $averagetotal * 100;
# jetzt werden diejenigen datafiles ermittelt, die aus der reihe tanzen
# wie verhaelt sich ihre differenz zum mittelwert zur standardabweichung
foreach my $datafile (@{$self->{datafiles}}) {
my $delta = abs($datafile->{io_total} - $averagetotal);
my $factor = $delta / $sx * 100;
$worstfactor = $factor unless $factor <= $worstfactor;
if ($self->check_thresholds($factor, 50, 100)) {
push(@unbalanced_datafiles, $datafile);
}
}
if ($self->check_thresholds($worstfactor, 50, 100)) {
$self->add_nagios($self->check_thresholds($worstfactor, 50, 100),
sprintf "%s datafiles %s io unbalanced (%f)", $self->{name},
join(",", map { $_->{name} } @unbalanced_datafiles), $worstfactor);
} else {
$self->add_nagios_ok(sprintf "%s datafiles io is well balanced",
$self->{name});
}
}
# coefficient of variation (cv)
$self->add_perfdata(sprintf "\'tbs_%s_io_cv\'=%.2f%%;%.2f;%.2f",
$self->{name}, $cv,
$self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~
/server::database::tablespace::remainingfreetime/) {
my $lookback = $params{lookback} || 30;
my $enoughvalues = 0;
my $newest = time - $lookback * 24 * 3600;
my @tmp = grep { $_->[0] > $newest } @{$self->{usage_history}};
$self->trace(sprintf "found %d usable data sets since %s",
scalar(@tmp), scalar localtime($newest));
if ((scalar(@{$self->{usage_history}}) - scalar(@tmp) > 0) &&
(scalar(@tmp) >= 2)) {
# only if more than two values are available
# only if we have data really reaching back some days
# predicting with two values from the last hour makes no sense
$self->{usage_history} = \@tmp;
my $remaining = 99999;
my $now = time; # normalisieren, so dass jetzt x=0
my $n = 0; my $sumx = 0; my $sumx2 = 0; my $sumxy = 0; my $sumy = 0; my $sumy2 = 0; my $m = 0; my $r = 0;
my $start_usage = undef;
my $stop_usage = undef;
foreach (@{$self->{usage_history}}) {
next if $_->[0] < $newest;
$start_usage = $_->[1] if ! defined $start_usage;
$stop_usage = $_->[1];
my $x = ($_->[0] - $now) / (24 * 3600);
my $y = $_->[1];
$n++; # increment number of data points by 1
$sumx += $x; # compute sum of x
$sumx2 += $x * $x; # compute sum of x**2
$sumxy += $x * $y; # compute sum of x * y
$sumy += $y; # compute sum of y
$sumy2 += $y * $y; # compute sum of y**2
}
# compute slope
$m = ($n * $sumxy - $sumx * $sumy) / ($n * $sumx2 - $sumx ** 2);
# compute y-intercept
$b = ($sumy * $sumx2 - $sumx * $sumxy) / ($n * $sumx2 - $sumx ** 2);
# compute correlation coefficient
#$r = ($sumxy - $sumx * $sumy / $n) /
# sqrt(($sumx2 - ($sumx ** 2)/$n) * ($sumy2 - ($sumy ** 2)/$n));
$self->debug(sprintf "slope: %f y-intersect: %f", $m, $b);
if (abs($m) <= 0.000001) { # $m == 0 does not work even if $m is 0.000000
$self->add_nagios_ok("tablespace usage is constant");
} elsif ($m > 0) {
$remaining = (100 - $b) / $m;
$self->add_nagios($self->check_thresholds($remaining, "90:", "30:"),
sprintf "tablespace %s will be full in %d days",
$self->{name}, $remaining);
$self->add_perfdata(sprintf "\'tbs_%s_days_until_full\'=%d;%s;%s",
lc $self->{name},
$remaining,
$self->{warningrange}, $self->{criticalrange});
} else {
$self->add_nagios_ok("tablespace usage is decreasing");
}
} else {
$self->add_nagios_ok("no data available for prediction");
}
} elsif ($params{mode} =~
/server::database::tablespace::segment::extendspace/) {
my $segments = 0;
my @largesegments = ();
foreach my $segment (@{$self->{segments}}) {
$segments++;
$segment->nagios(%params);
if ($segment->{nagios_level}) {
push(@largesegments, $segment->{name});
}
#$self->merge_nagios($segment);
}
if (! $segments) {
$self->add_nagios_ok(
sprintf "tablespace %s has no segments", $self->{name});
} elsif (@largesegments) {
if ($self->{allocation_type} ne "SYSTEM") {
$self->add_nagios_critical(
sprintf "tablespace %s cannot extend segment(s) %s", $self->{name},
join(", ", @largesegments));
} else {
$self->add_nagios_ok(
sprintf "tablespace %s free extents are large enough (autoallocate)",
$self->{name});
}
} elsif (! $self->{nagios_level}) {
$self->add_nagios_ok(
sprintf "tablespace %s free extents are large enough",
$self->{name});
}
} elsif ($params{mode} =~ /server::database::tablespace::datafile/) {
printf "%s\n", $self->dump();
}
}
}
package DBD::Oracle::Server::Database;
use strict;
our @ISA = qw(DBD::Oracle::Server);
sub new {
my $class = shift;
my %params = @_;
my $self = {
handle => $params{handle},
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
invalidobjects => {
invalid_objects => undef,
invalid_indexes => undef,
invalid_ind_partitions => undef,
},
staleobjects => undef,
tablespaces => [],
};
bless $self, $class;
$self->init(%params);
return $self;
}
sub init {
my $self = shift;
my %params = @_;
$self->init_nagios();
if ($params{mode} =~ /server::database::tablespace/) {
DBD::Oracle::Server::Database::Tablespace::init_tablespaces(%params);
if (my @tablespaces =
DBD::Oracle::Server::Database::Tablespace::return_tablespaces()) {
$self->{tablespaces} = \@tablespaces;
} else {
$self->add_nagios_critical("unable to aquire tablespace info");
}
} elsif ($params{mode} =~ /server::database::invalidobjects/) {
$self->init_invalid_objects(%params);
} elsif ($params{mode} =~ /server::database::stalestats/) {
$self->init_stale_objects(%params);
}
}
sub init_invalid_objects {
my $self = shift;
my %params = @_;
my $invalid_objects = undef;
my $invalid_indexes = undef;
my $invalid_ind_partitions = undef;
my $unrecoverable_datafiles = undef;
$self->{invalidobjects}->{invalid_objects} =
$self->{handle}->fetchrow_array(q{
SELECT COUNT(*)
FROM dba_objects
WHERE status = 'INVALID' AND object_name NOT LIKE 'BIN$%'
AND object_type NOT IN ('MATERIALIZED VIEW')
});
# should be only N/A or VALID
$self->{invalidobjects}->{invalid_indexes} =
$self->{handle}->fetchrow_array(q{
SELECT COUNT(DISTINCT OWNER||'.'||INDEX_NAME)
FROM dba_indexes
WHERE status <> 'VALID' AND status <> 'N/A'
});
# should be only USABLE or N/A
$self->{invalidobjects}->{invalid_ind_partitions} =
$self->{handle}->fetchrow_array(q{
SELECT COUNT(DISTINCT INDEX_OWNER||'.'||INDEX_NAME)
FROM dba_ind_partitions
WHERE status <> 'USABLE' AND status <> 'N/A'
});
if (! defined $self->{invalidobjects}->{invalid_objects} ||
! defined $self->{invalidobjects}->{invalid_indexes} ||
! defined $self->{invalidobjects}->{invalid_ind_partitions}) {
$self->add_nagios_critical("unable to get invalid objects");
return undef;
}
}
sub init_stale_objects {
my $self = shift;
my %params = @_;
if ($self->version_is_minimum("10.x")) {
$self->{staleobjects} = $self->{handle}->fetchrow_array(q{
SELECT COUNT(*) FROM sys.dba_tab_statistics WHERE stale_stats = 'YES' AND table_name not like 'BIN$%'
});
} else {
# oracle9 + sqlplus nix gut
$self->{handle}->func( 10000, 'dbms_output_enable' );
$self->{handle}->execute(q{
DECLARE
l_objList dbms_stats.objectTab;
BEGIN
DBMS_OUTPUT.ENABLE (1000000);
dbms_stats.gather_database_stats(
options => 'LIST STALE',
objlist => l_objList );
dbms_output.put_line( l_objList.COUNT);
-- FOR i IN 1 .. l_objList.COUNT
-- LOOP
-- dbms_output.put_line( l_objList(i).objType );
-- dbms_output.put_line( l_objList(i).objName );
-- END LOOP;
END;
});
$self->{staleobjects} = $self->{handle}->func( 'dbms_output_get' );
}
if (! defined $self->{staleobjects}) {
$self->add_nagios_critical("unable to get stale objects");
return undef;
}
}
sub nagios {
my $self = shift;
my %params = @_;
if (! $self->{nagios_level}) {
if ($params{mode} =~ /server::database::tablespace::listtablespaces/) {
foreach (sort { $a->{name} cmp $b->{name}; } @{$self->{tablespaces}}) {
printf "%s\n", $_->{name};
}
$self->add_nagios_ok("have fun");
} elsif ($params{mode} =~ /server::database::tablespace/) {
foreach (@{$self->{tablespaces}}) {
# sind hier noch nach pctused sortiert
$_->nagios(%params);
$self->merge_nagios($_);
}
} elsif ($params{mode} =~ /server::database::invalidobjects/) {
my @message = ();
push(@message, sprintf "%d invalid objects",
$self->{invalidobjects}->{invalid_objects}) if
$self->{invalidobjects}->{invalid_objects};
push(@message, sprintf "%d invalid indexes",
$self->{invalidobjects}->{invalid_indexes}) if
$self->{invalidobjects}->{invalid_indexes};
push(@message, sprintf "%d invalid index partitions",
$self->{invalidobjects}->{invalid_ind_partitions}) if
$self->{invalidobjects}->{invalid_ind_partitions};
if (scalar(@message)) {
$self->add_nagios($self->check_thresholds(
$self->{invalidobjects}->{invalid_objects} +
$self->{invalidobjects}->{invalid_indexes} +
$self->{invalidobjects}->{invalid_ind_partitions}, 0.1, 0.1),
join(", ", @message));
} else {
$self->add_nagios_ok("no invalid objects found");
}
foreach (sort keys %{$self->{invalidobjects}}) {
$self->add_perfdata(sprintf "%s=%d", $_, $self->{invalidobjects}->{$_});
}
} elsif ($params{mode} =~ /server::database::stalestats/) {
$self->add_nagios(
$self->check_thresholds($self->{staleobjects}, "10", "100"),
sprintf "%d objects with stale statistics", $self->{staleobjects});
$self->add_perfdata(sprintf "stale_stats_objects=%d;%s;%s",
$self->{staleobjects},
$self->{warningrange}, $self->{criticalrange});
}
}
}
package DBD::Oracle::Server;
use strict;
use Time::HiRes;
use IO::File;
use File::Copy 'cp';
use Data::Dumper;
{
our $verbose = 0;
our $scream = 0; # scream if something is not implemented
our $access = "dbi"; # how do we access the database.
our $my_modules_dyn_dir = ""; # where we look for self-written extensions
my @servers = ();
my $initerrors = undef;
sub add_server {
push(@servers, shift);
}
sub return_servers {
return @servers;
}
sub return_first_server() {
return $servers[0];
}
}
sub new {
my $class = shift;
my %params = @_;
my $self = {
access => $params{method} || "dbi",
connect => $params{connect},
username => $params{username},
password => $params{password},
timeout => $params{timeout},
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
version => 'unknown',
instance => undef,
database => undef,
handle => undef,
};
bless $self, $class;
$self->init_nagios();
if ($self->dbconnect(%params)) {
$self->{version} = $self->{handle}->fetchrow_array(
q{ SELECT version FROM v$instance });
$self->{os} = $self->{handle}->fetchrow_array(
q{ SELECT dbms_utility.port_string FROM dual });
$self->{dbuser} = $self->{handle}->fetchrow_array(
q{ SELECT sys_context('userenv', 'session_user') FROM dual });
$self->{thread} = $self->{handle}->fetchrow_array(
q{ SELECT thread# FROM v$instance });
$self->{parallel} = $self->{handle}->fetchrow_array(
q{ SELECT parallel FROM v$instance });
DBD::Oracle::Server::add_server($self);
$self->init(%params);
}
return $self;
}
sub init {
my $self = shift;
my %params = @_;
$params{handle} = $self->{handle};
if ($params{mode} =~ /^server::instance/) {
$self->{instance} = DBD::Oracle::Server::Instance->new(%params);
} elsif ($params{mode} =~ /^server::database/) {
$self->{database} = DBD::Oracle::Server::Database->new(%params);
} elsif ($params{mode} =~ /^server::sql/) {
@{$self->{genericsql}} =
$self->{handle}->fetchrow_array($params{selectname});
if (! (defined $self->{genericsql} &&
(scalar(grep { /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/ } @{$self->{genericsql}})) ==
scalar(@{$self->{genericsql}}))) {
$self->add_nagios_unknown(sprintf "got no valid response for %s",
$params{selectname});
} else {
# name2 in array
# units in array
}
} elsif ($params{mode} =~ /^server::connectiontime/) {
$self->{connection_time} = $self->{tac} - $self->{tic};
} elsif ($params{mode} =~ /^my::([^:.]+)/) {
my $class = $1;
my $loaderror = undef;
substr($class, 0, 1) = uc substr($class, 0, 1);
foreach my $libpath (split(":", $DBD::Oracle::Server::my_modules_dyn_dir)) {
foreach my $extmod (glob $libpath."/CheckOracleHealth*.pm") {
eval {
$self->trace(sprintf "loading module %s", $extmod);
require $extmod;
};
if ($@) {
$loaderror = $extmod;
$self->trace(sprintf "failed loading module %s: %s", $extmod, $@);
}
}
}
my $obj = {
handle => $params{handle},
warningrange => $params{warningrange},
criticalrange => $params{criticalrange},
};
bless $obj, "My$class";
$self->{my} = $obj;
if ($self->{my}->isa("DBD::Oracle::Server")) {
my $dos_init = $self->can("init");
my $dos_nagios = $self->can("nagios");
my $my_init = $self->{my}->can("init");
my $my_nagios = $self->{my}->can("nagios");
if ($my_init == $dos_init) {
$self->add_nagios_unknown(
sprintf "Class %s needs an init() method", ref($self->{my}));
} elsif ($my_nagios == $dos_nagios) {
$self->add_nagios_unknown(
sprintf "Class %s needs a nagios() method", ref($self->{my}));
} else {
$self->{my}->init_nagios(%params);
$self->{my}->init(%params);
}
} else {
$self->add_nagios_unknown(
sprintf "Class %s is not a subclass of DBD::Oracle::Server%s",
ref($self->{my}),
$loaderror ? sprintf " (syntax error in %s?)", $loaderror : "" );
}
} else {
printf "broken mode %s\n", $params{mode};
}
}
sub dump {
my $self = shift;
my $message = shift || "";
printf "%s %s\n", $message, Data::Dumper::Dumper($self);
}
sub nagios {
my $self = shift;
my %params = @_;
if (! $self->{nagios_level}) {
if ($params{mode} =~ /^server::instance/) {
$self->{instance}->nagios(%params);
$self->merge_nagios($self->{instance});
} elsif ($params{mode} =~ /^server::database/) {
$self->{database}->nagios(%params);
$self->merge_nagios($self->{database});
} elsif ($params{mode} =~ /^server::connectiontime/) {
$self->add_nagios(
$self->check_thresholds($self->{connection_time}, 1, 5),
sprintf "%.2f seconds to connect as %s",
$self->{connection_time}, $self->{dbuser});
$self->add_perfdata(sprintf "connection_time=%.4f;%d;%d",
$self->{connection_time},
$self->{warningrange}, $self->{criticalrange});
} elsif ($params{mode} =~ /^server::sql/) {
$self->add_nagios(
# the first item in the list will trigger the threshold values
$self->check_thresholds($self->{genericsql}[0], 1, 5),
sprintf "%s: %s%s",
$params{name2} ? lc $params{name2} : lc $params{selectname},
# float as float, integers as integers
join(" ", map {
(sprintf("%d", $_) eq $_) ? $_ : sprintf("%f", $_)
} @{$self->{genericsql}}),
$params{units} ? $params{units} : "");
my $i = 0;
# workaround... getting the column names from the database would be nicer
my @names2_arr = split(/\s+/, $params{name2});
foreach my $t (@{$self->{genericsql}}) {
$self->add_perfdata(sprintf "\'%s\'=%s%s;%s;%s",
$names2_arr[$i] ? lc $names2_arr[$i] : lc $params{selectname},
# float as float, integers as integers
(sprintf("%d", $t) eq $t) ? $t : sprintf("%f", $t),
$params{units} ? $params{units} : "",
($i == 0) ? $self->{warningrange} : "",
($i == 0) ? $self->{criticalrange} : ""
);
$i++;
}
} elsif ($params{mode} =~ /^my::([^:.]+)/) {
$self->{my}->nagios(%params);
$self->merge_nagios($self->{my});
}
}
}
sub init_nagios {
my $self = shift;
no strict 'refs';
if (! ref($self)) {
my $nagiosvar = $self."::nagios";
my $nagioslevelvar = $self."::nagios_level";
$$nagiosvar = {
messages => {
0 => [],
1 => [],
2 => [],
3 => [],
},
perfdata => [],
};
$$nagioslevelvar = $ERRORS{OK},
} else {
$self->{nagios} = {
messages => {
0 => [],
1 => [],
2 => [],
3 => [],
},
perfdata => [],
};
$self->{nagios_level} = $ERRORS{OK},
}
}
sub check_thresholds {
my $self = shift;
my $value = shift;
my $defaultwarningrange = shift;
my $defaultcriticalrange = shift;
my $level = $ERRORS{OK};
$self->{warningrange} = defined $self->{warningrange} ?
$self->{warningrange} : $defaultwarningrange;
$self->{criticalrange} = defined $self->{criticalrange} ?
$self->{criticalrange} : $defaultcriticalrange;
if ($self->{warningrange} !~ /:/ && $self->{criticalrange} !~ /:/) {
# warning = 10, critical = 20, warn if > 10, crit if > 20
$level = $ERRORS{WARNING} if $value > $self->{warningrange};
$level = $ERRORS{CRITICAL} if $value > $self->{criticalrange};
} elsif ($self->{warningrange} =~ /(\d+):/ &&
$self->{criticalrange} =~ /(\d+):/) {
# warning = 98:, critical = 95:, warn if < 98, crit if < 95
$self->{warningrange} =~ /(\d+):/;
$level = $ERRORS{WARNING} if $value < $1;
$self->{criticalrange} =~ /(\d+):/;
$level = $ERRORS{CRITICAL} if $value < $1;
}
return $level;
#
# syntax error must be reported with returncode -1
#
}
sub add_nagios {
my $self = shift;
my $level = shift;
my $message = shift;
push(@{$self->{nagios}->{messages}->{$level}}, $message);
# recalc current level
foreach my $llevel qw(CRITICAL WARNING UNKNOWN OK) {
if (scalar(@{$self->{nagios}->{messages}->{$ERRORS{$llevel}}})) {
$self->{nagios_level} = $ERRORS{$llevel};
}
}
}
sub add_nagios_ok {
my $self = shift;
my $message = shift;
$self->add_nagios($ERRORS{OK}, $message);
}
sub add_nagios_warning {
my $self = shift;
my $message = shift;
$self->add_nagios($ERRORS{WARNING}, $message);
}
sub add_nagios_critical {
my $self = shift;
my $message = shift;
$self->add_nagios($ERRORS{CRITICAL}, $message);
}
sub add_nagios_unknown {
my $self = shift;
my $message = shift;
$self->add_nagios($ERRORS{UNKNOWN}, $message);
}
sub add_perfdata {
my $self = shift;
my $data = shift;
push(@{$self->{nagios}->{perfdata}}, $data);
}
sub merge_nagios {
my $self = shift;
my $child = shift;
foreach my $level (0..3) {
foreach (@{$child->{nagios}->{messages}->{$level}}) {
$self->add_nagios($level, $_);
}
#push(@{$self->{nagios}->{messages}->{$level}},
# @{$child->{nagios}->{messages}->{$level}});
}
push(@{$self->{nagios}->{perfdata}}, @{$child->{nagios}->{perfdata}});
}
sub calculate_result {
my $self = shift;
if ($ENV{NRPE_MULTILINESUPPORT} &&
length join(" ", @{$self->{nagios}->{perfdata}}) > 200) {
foreach my $level ("CRITICAL", "WARNING", "UNKNOWN", "OK") {
# first the bad news
if (scalar(@{$self->{nagios}->{messages}->{$ERRORS{$level}}})) {
$self->{nagios_message} .=
"\n".join("\n", @{$self->{nagios}->{messages}->{$ERRORS{$level}}});
}
}
$self->{nagios_message} =~ s/^\n//g;
$self->{perfdata} = join("\n", @{$self->{nagios}->{perfdata}});
} else {
foreach my $level ("CRITICAL", "WARNING", "UNKNOWN", "OK") {
# first the bad news
if (scalar(@{$self->{nagios}->{messages}->{$ERRORS{$level}}})) {
$self->{nagios_message} .=
join(", ", @{$self->{nagios}->{messages}->{$ERRORS{$level}}}).", ";
}
}
$self->{nagios_message} =~ s/, $//g;
$self->{perfdata} = join(" ", @{$self->{nagios}->{perfdata}});
}
foreach my $level ("OK", "UNKNOWN", "WARNING", "CRITICAL") {
if (scalar(@{$self->{nagios}->{messages}->{$ERRORS{$level}}})) {
$self->{nagios_level} = $ERRORS{$level};
}
}
}
sub debug {
my $self = shift;
my $msg = shift;
if ($DBD::Oracle::Server::verbose) {
printf "%s %s\n", $msg, ref($self);
}
}
sub dbconnect {
my $self = shift;
my %params = @_;
my $retval = undef;
$self->{tic} = Time::HiRes::time();
$self->{handle} = DBD::Oracle::Server::Connection->new(%params);
if ($self->{handle}->{errstr}) {
if ($params{mode} =~ /^server::tnsping/ &&
$self->{handle}->{errstr} =~ /ORA-01017/) {
$self->add_nagios($ERRORS{OK},
sprintf "connection established to %s.", $self->{connect});
$retval = undef;
} elsif ($self->{handle}->{errstr} eq "alarm\n") {
$self->add_nagios($ERRORS{CRITICAL},
sprintf "connection could not be established within %d seconds",
$self->{timeout});
} else {
if ($self->{connect} =~ /^(.*?)\/(.*)@(.*)$/) {
$self->{connect} = sprintf "%s/***@%s", $1, $3;
} elsif ($self->{connect} =~ /^(.*?)@(.*)$/) {
$self->{connect} = sprintf "%s@%s", $1, $2;
}
$self->add_nagios($ERRORS{CRITICAL},
sprintf "cannot connect to %s. %s",
$self->{connect}, $self->{handle}->{errstr});
$retval = undef;
}
} else {
$retval = $self->{handle};
}
$self->{tac} = Time::HiRes::time();
return $retval;
}
sub trace {
my $self = shift;
my $format = shift;
$self->{trace} = -f "/tmp/check_oracle_health.trace" ? 1 : 0;
if ($self->{verbose}) {
printf("%s: ", scalar localtime);
printf($format, @_);
}
if ($self->{trace}) {
my $logfh = new IO::File;
$logfh->autoflush(1);
if ($logfh->open("/tmp/check_oracle_health.trace", "a")) {
$logfh->printf("%s: ", scalar localtime);
$logfh->printf($format, @_);
$logfh->printf("\n");
$logfh->close();
}
}
}
sub DESTROY {
my $self = shift;
my $handle1 = "null";
my $handle2 = "null";
if (defined $self->{handle}) {
$handle1 = ref($self->{handle});
if (defined $self->{handle}->{handle}) {
$handle2 = ref($self->{handle}->{handle});
}
}
#$self->trace(sprintf "DESTROY %s with handle %s %s", ref($self), $handle1, $handle2);
if (ref($self) eq "DBD::Oracle::Server") {
}
#$self->trace(sprintf "DESTROY %s exit with handle %s %s", ref($self), $handle1, $handle2);
if (ref($self) eq "DBD::Oracle::Server") {
#printf "humpftata\n";
}
}
sub save_state {
my $self = shift;
my %params = @_;
my $extension = "";
if ($params{connect} =~ /(\w+)\/(\w+)@(\w+)/) {
$params{connect} = $3;
} else {
# just to be sure
$params{connect} =~ s/\//_/g;
}
mkdir $params{statefilesdir} unless -d $params{statefilesdir};
my $statefile = sprintf "%s/%s_%s",
$params{statefilesdir}, $params{connect}, $params{mode};
$extension .= $params{differenciator} ? "_".$params{differenciator} : "";
$extension .= $params{tablespace} ? "_".$params{tablespace} : "";
$extension .= $params{datafile} ? "_".$params{datafile} : "";
$extension .= $params{name} ? "_".$params{name} : "";
$extension =~ s/\//_/g;
$extension =~ s/\(/_/g;
$extension =~ s/\)/_/g;
$extension =~ s/\*/_/g;
$extension =~ s/\s/_/g;
$statefile .= $extension;
$statefile = lc $statefile;
open(STATE, ">$statefile");
if ((ref($params{save}) eq "HASH") && exists $params{save}->{timestamp}) {
$params{save}->{localtime} = scalar localtime $params{save}->{timestamp};
}
printf STATE Data::Dumper::Dumper($params{save});
close STATE;
$self->debug(sprintf "saved %s to %s",
Data::Dumper::Dumper($params{save}), $statefile);
}
sub load_state {
my $self = shift;
my %params = @_;
my $extension = "";
if ($params{connect} =~ /(\w+)\/(\w+)@(\w+)/) {
$params{connect} = $3;
} else {
$params{connect} =~ s/\//_/g;
}
my $statefile = sprintf "%s/%s_%s",
$params{statefilesdir}, $params{connect}, $params{mode};
$extension .= $params{differenciator} ? "_".$params{differenciator} : "";
$extension .= $params{tablespace} ? "_".$params{tablespace} : "";
$extension .= $params{datafile} ? "_".$params{datafile} : "";
$extension .= $params{name} ? "_".$params{name} : "";
$extension =~ s/\//_/g;
$extension =~ s/\(/_/g;
$extension =~ s/\)/_/g;
$extension =~ s/\*/_/g;
$extension =~ s/\s/_/g;
$statefile .= $extension;
$statefile = lc $statefile;
if ( -f $statefile) {
our $VAR1;
eval {
require $statefile;
};
if($@) {
printf "rumms\n";
}
$self->debug(sprintf "load %s", Data::Dumper::Dumper($VAR1));
return $VAR1;
} else {
return undef;
}
}
sub valdiff {
my $self = shift;
my $pparams = shift;
my %params = %{$pparams};
my @keys = @_;
my $last_values = $self->load_state(%params) || eval {
my $empty_events = {};
foreach (@keys) {
$empty_events->{$_} = 0;
}
$empty_events->{timestamp} = 0;
$empty_events;
};
foreach (@keys) {
$last_values->{$_} = 0 if ! exists $last_values->{$_};
if ($self->{$_} >= $last_values->{$_}) {
$self->{'delta_'.$_} = $self->{$_} - $last_values->{$_};
} else {
# vermutlich db restart und zaehler alle auf null
$self->{'delta_'.$_} = $self->{$_};
}
$self->debug(sprintf "delta_%s %f", $_, $self->{'delta_'.$_});
}
$self->{'delta_timestamp'} = time - $last_values->{timestamp};
$params{save} = eval {
my $empty_events = {};
foreach (@keys) {
$empty_events->{$_} = $self->{$_};
}
$empty_events->{timestamp} = time;
$empty_events;
};
$self->save_state(%params);
}
sub requires_version {
my $self = shift;
my $version = shift;
my @instances = DBD::Oracle::Server::return_servers();
my $instversion = $instances[0]->{version};
if (! $self->version_is_minimum($version)) {
$self->add_nagios($ERRORS{UNKNOWN},
sprintf "not implemented/possible for Oracle release %s", $instversion);
}
}
sub version_is_minimum {
# the current version is newer or equal
my $self = shift;
my $version = shift;
my $newer = 1;
my @instances = DBD::Oracle::Server::return_servers();
my @v1 = map { $_ eq "x" ? 0 : $_ } split(/\./, $version);
my @v2 = split(/\./, $instances[0]->{version});
if (scalar(@v1) > scalar(@v2)) {
push(@v2, (0) x (scalar(@v1) - scalar(@v2)));
} elsif (scalar(@v2) > scalar(@v1)) {
push(@v1, (0) x (scalar(@v2) - scalar(@v1)));
}
foreach my $pos (0..$#v1) {
if ($v2[$pos] > $v1[$pos]) {
$newer = 1;
last;
} elsif ($v2[$pos] < $v1[$pos]) {
$newer = 0;
last;
}
}
#printf STDERR "check if %s os minimum %s\n", join(".", @v2), join(".", @v1);
return $newer;
}
sub instance_rac {
my $self = shift;
my @instances = DBD::Oracle::Server::return_servers();
return (lc $instances[0]->{parallel} eq "yes") ? 1 : 0;
}
sub instance_thread {
my $self = shift;
my @instances = DBD::Oracle::Server::return_servers();
return $instances[0]->{thread};
}
sub windows_server {
my $self = shift;
my @instances = DBD::Oracle::Server::return_servers();
if ($instances[0]->{os} =~ /Win/i) {
return 1;
} else {
return 0;
}
}
sub system_vartmpdir {
my $self = shift;
if ($^O =~ /MSWin/) {
return $self->system_tmpdir();
} else {
return "/var/tmp/check_oracle_health";
}
}
sub system_oldvartmpdir {
my $self = shift;
return "/tmp";
}
sub system_tmpdir {
my $self = shift;
if ($^O =~ /MSWin/) {
return $ENV{TEMP} if defined $ENV{TEMP};
return $ENV{TMP} if defined $ENV{TMP};
return File::Spec->catfile($ENV{windir}, 'Temp')
if defined $ENV{windir};
return 'C:\Temp';
} else {
return "/tmp";
}
}
package DBD::Oracle::Server::Connection;
use strict;
our @ISA = qw(DBD::Oracle::Server);
sub new {
my $class = shift;
my %params = @_;
my $self = {
mode => $params{mode},
timeout => $params{timeout},
access => $params{method} || "dbi",
connect => $params{connect},
username => $params{username},
password => $params{password},
tnsadmin => $ENV{TNS_ADMIN},
oraclehome => $ENV{ORACLE_HOME},
handle => undef,
};
bless $self, $class;
if ($params{method} eq "dbi") {
bless $self, "DBD::Oracle::Server::Connection::Dbi";
} elsif ($params{method} eq "sqlplus") {
bless $self, "DBD::Oracle::Server::Connection::Sqlplus";
} elsif ($params{method} eq "sqlrelay") {
bless $self, "DBD::Oracle::Server::Connection::Sqlrelay";
}
$self->init(%params);
return $self;
}
package DBD::Oracle::Server::Connection::Dbi;
use strict;
use Net::Ping;
our @ISA = qw(DBD::Oracle::Server::Connection);
sub init {
my $self = shift;
my %params = @_;
my $retval = undef;
if ($self->{mode} =~ /^server::tnsping/) {
if (! $self->{connect}) {
$self->{errstr} = "Please specify a database";
} else {
$self->{sid} = $self->{connect};
$self->{username} ||= time; # prefer an existing user
$self->{password} ||= time;
}
} else {
if (! $self->{connect} || ! $self->{username} || ! $self->{password}) {
if ($self->{connect} && $self->{connect} =~ /(\w+)\/(\w+)@(\w+)/) {
$self->{connect} = $3;
$self->{username} = $1;
$self->{password} = $2;
$self->{sid} = $self->{connect};
} elsif ($self->{connect} && $self->{connect} =~ /^(sys|sysdba)@(\w+)/) {
$self->{connect} = $2;
$self->{username} = $1;
$self->{password} = '';
$self->{sid} = $self->{connect};
} elsif ($self->{connect} && ! $self->{username} && ! $self->{password}) {
# maybe this is a os authenticated user
delete $ENV{TWO_TASK};
$self->{sid} = $self->{connect};
if ($^O ne "hpux") { #hpux && 1.21 only accepts "DBI:Oracle:SID"
$self->{connect} = ""; #linux 1.20 only accepts "DBI:Oracle:" + ORACLE_SID
}
$self->{username} = '/';
$self->{password} = "";
} else {
$self->{errstr} = "Please specify database, username and password";
return undef;
}
} else {
$self->{sid} = $self->{connect};
}
}
if (! exists $self->{errstr}) {
$ENV{ORACLE_SID} = $self->{sid};
eval {
require DBI;
use POSIX ':signal_h';
local $SIG{'ALRM'} = sub {
die "alarm\n";
};
my $mask = POSIX::SigSet->new( SIGALRM );
my $action = POSIX::SigAction->new(
sub { die "alarm\n" ; }, $mask);
my $oldaction = POSIX::SigAction->new();
sigaction(SIGALRM ,$action ,$oldaction );
alarm($self->{timeout} - 1); # 1 second before the global unknown timeout
my $dsn = sprintf "DBI:Oracle:%s", $self->{connect};
my $connecthash = { RaiseError => 0, AutoCommit => 0, PrintError => 0 };
if ($self->{username} eq "sys" || $self->{username} eq "sysdba") {
$connecthash = { RaiseError => 0, AutoCommit => 0, PrintError => 0,
#ora_session_mode => DBD::Oracle::ORA_SYSDBA
ora_session_mode => 0x0002 };
$dsn = sprintf "DBI:Oracle:";
}
if ($self->{handle} = DBI->connect(
$dsn,
$self->{username},
$self->{password},
$connecthash)) {
$self->{handle}->do(q{
ALTER SESSION SET NLS_NUMERIC_CHARACTERS=".," });
$retval = $self;
if ($self->{mode} =~ /^server::tnsping/) {
$self->{handle}->disconnect();
die "ORA-01017"; # fake a successful connect with wrong password
}
} else {
$self->{errstr} = DBI::errstr();
}
};
if ($@) {
$self->{errstr} = $@;
$retval = undef;
}
}
$self->{tac} = Time::HiRes::time();
return $retval;
}
sub tnsping {
my $self = shift;
my $retval = undef;
if ($self->{tnsadmin}) {
$self->{tnsfile} = $self->{tnsadmin}.'/tnanames.ora';
} elsif ($self->{oraclehome}) {
$self->{tnsfile} = $self->{oraclehome}.'/network/admin/tnsnames.ora';
} else {
$self->{tnsfile} = $ENV{HOME}.'/tnsnames.ora';
}
my $re_blank = '^$';
my $re_comment = '^#';
my $re_tns_sentry = '^'.$self->{sid}.'.*?='; # specific entry
my $re_tns_gentry = '^\w.*?='; # generic entry
my $re_tns_pair = '\w+\s*\=\s*[\w\.]+'; # used to parse key=val
my $re_keys = 'host|port|sid';
my @tnsfile = split(/\n/, do { local (@ARGV, $/) = $self->{tnsfile}; <> });
my $found = 0;
my $datastring = "";
foreach (@tnsfile) {
chomp;
next if /$re_blank/;
next if /$re_comment/;
if (/$re_tns_sentry/) {
$found = 1;
s/$re_tns_sentry//;
$datastring = $_;
}
if ($found) {
last if /$re_tns_gentry/;
$datastring .= $_;
}
}
if ($found) {
while ($datastring =~ m/($re_tns_pair)/g) {
my ($key, $value) = split(/=/, $1);
$key =~ s/^\s+//g;
$value =~ s/^\s+//g;
$key =~ s/\s+$//g;
$value =~ s/\s+$//g;
next unless $key =~ /$re_keys/i;
if (lc $key eq "host") {
$self->{hostname} = $value;
} elsif (lc $key eq "port") {
$self->{port} = $value;
}
}
}
if (exists $self->{hostname} && exists $self->{port}) {
my $p = Net::Ping->new("tcp");
$p->{port_num} = $self->{port};
if ($p->ping($self->{hostname}, $self->{timeout} - 1)) {
$self->{handle} = 1;
$retval = $self;
} else {
$self->{errstr} = sprintf "tnsping timed out after %d seconds",
$self->{timeout};
}
} else {
$self->{errstr} = sprintf "could not find host and address for %s",
$self->{sid};
}
return $retval;
}
sub fetchrow_array {
my $self = shift;
my $sql = shift;
my @arguments = @_;
my $sth = undef;
my @row = ();
$self->trace(sprintf "fetchrow_array: %s", $sql);
$self->trace(sprintf "args: %s", Data::Dumper::Dumper(\@arguments));
eval {
$sth = $self->{handle}->prepare($sql);
if (scalar(@arguments)) {
$sth->execute(@arguments);
} else {
$sth->execute();
}
@row = $sth->fetchrow_array();
};
if ($@) {
$self->debug(sprintf "bumm %s", $@);
}
if (-f "/tmp/check_oracle_health_simulation/".$self->{mode}) {
my $simulation = do { local (@ARGV, $/) =
"/tmp/check_oracle_health_simulation/".$self->{mode}; <> };
@row = split(/\s+/, (split(/\n/, $simulation))[0]);
}
return $row[0] unless wantarray;
return @row;
}
sub fetchall_array {
my $self = shift;
my $sql = shift;
my @arguments = @_;
my $sth = undef;
my $rows = undef;
$self->trace(sprintf "fetchall_array: %s", $sql);
$self->trace(sprintf "args: %s", Data::Dumper::Dumper(\@arguments));
eval {
$sth = $self->{handle}->prepare($sql);
if (scalar(@arguments)) {
$sth->execute(@arguments);
} else {
$sth->execute();
}
$rows = $sth->fetchall_arrayref();
};
if ($@) {
printf STDERR "bumm %s\n", $@;
}
if (-f "/tmp/check_oracle_health_simulation/".$self->{mode}) {
my $simulation = do { local (@ARGV, $/) =
"/tmp/check_oracle_health_simulation/".$self->{mode}; <> };
@{$rows} = map { [ split(/\s+/, $_) ] } split(/\n/, $simulation);
}
return @{$rows};
}
sub func {
my $self = shift;
$self->{handle}->func(@_);
}
sub execute {
my $self = shift;
my $sql = shift;
eval {
my $sth = $self->{handle}->prepare($sql);
$sth->execute();
};
if ($@) {
printf STDERR "bumm %s\n", $@;
}
}
sub DESTROY {
my $self = shift;
$self->trace(sprintf "disconnecting DBD %s",
$self->{handle} ? "with handle" : "without handle");
$self->{handle}->disconnect() if $self->{handle};
}
package DBD::Oracle::Server::Connection::Sqlplus;
use strict;
use File::Temp qw/tempfile/;
our @ISA = qw(DBD::Oracle::Server::Connection);
sub init {
my $self = shift;
my %params = @_;
my $retval = undef;
$self->{loginstring} = "traditional";
($self->{sql_commandfile_handle}, $self->{sql_commandfile}) =
tempfile($self->{mode}."XXXXX", SUFFIX => ".sql",
DIR => $self->system_tmpdir() );
close $self->{sql_commandfile_handle};
($self->{sql_resultfile_handle}, $self->{sql_resultfile}) =
tempfile($self->{mode}."XXXXX", SUFFIX => ".out",
DIR => $self->system_tmpdir() );
close $self->{sql_resultfile_handle};
if ($self->{mode} =~ /^server::tnsping/) {
if (! $self->{connect}) {
$self->{errstr} = "Please specify a database";
} else {
$self->{sid} = $self->{connect};
$self->{username} ||= time; # prefer an existing user
$self->{password} = time;
}
} else {
if ($self->{connect} && ! $self->{username} && ! $self->{password} &&
$self->{connect} =~ /(\w+)\/(\w+)@(\w+)/) {
# --connect nagios/oradbmon@bba
$self->{connect} = $3;
$self->{username} = $1;
$self->{password} = $2;
$self->{sid} = $self->{connect};
if ($self->{username} eq "sys") {
delete $ENV{TWO_TASK};
$self->{loginstring} = "sys";
} else {
$self->{loginstring} = "traditional";
}
} elsif ($self->{connect} && ! $self->{username} && ! $self->{password} &&
$self->{connect} =~ /sysdba@(\w+)/) {
# --connect sysdba@bba
$self->{connect} = $1;
$self->{username} = "/";
$self->{sid} = $self->{connect};
$self->{loginstring} = "sysdba";
} elsif ($self->{connect} && ! $self->{username} && ! $self->{password} &&
$self->{connect} =~ /(\w+)/) {
# --connect bba
$self->{connect} = $1;
# maybe this is a os authenticated user
delete $ENV{TWO_TASK};
$self->{sid} = $self->{connect};
if ($^O ne "hpux") { #hpux && 1.21 only accepts "DBI:Oracle:SID"
$self->{connect} = ""; #linux 1.20 only accepts "DBI:Oracle:" + ORACLE_SID
}
$self->{username} = '/';
$self->{password} = "";
$self->{loginstring} = "extauth";
} elsif ($self->{username} &&
$self->{username} =~ /^\/@(\w+)/) {
# --user /@ubba1
$self->{username} = $1;
$self->{sid} = $self->{connect};
$self->{loginstring} = "passwordstore";
} elsif ($self->{connect} && $self->{username} && ! $self->{password} &&
$self->{username} eq "sysdba") {
# --connect bba --user sysdba
$self->{connect} = $1;
$self->{username} = "/";
$self->{sid} = $self->{connect};
$self->{loginstring} = "sysdba";
} elsif ($self->{connect} && $self->{username} && $self->{password}) {
# --connect bba --username nagios --password oradbmon
$self->{sid} = $self->{connect};
$self->{loginstring} = "traditional";
} else {
$self->{errstr} = "Please specify database, username and password";
return undef;
}
}
if (! exists $self->{errstr}) {
eval {
$ENV{ORACLE_SID} = $self->{sid};
$ENV{PATH} = $ENV{ORACLE_HOME}."/bin".
(defined $ENV{PATH} ?
":".$ENV{PATH} : "");
$ENV{LD_LIBRARY_PATH} = $ENV{ORACLE_HOME}."/lib".
(defined $ENV{LD_LIBRARY_PATH} ?
":".$ENV{LD_LIBRARY_PATH} : "");
# am 30.9.2008 hat perl das /bin/sqlplus in $ENV{ORACLE_HOME}.'/bin/sqlplus'
# eiskalt evaluiert und
# /u00/app/oracle/product/11.1.0/db_1/u00/app/oracle/product/11.1.0/db_1/bin/sqlplus
# draus gemacht. Leider nicht in Mini-Scripts reproduzierbar, sondern nur hier.
# Das ist der Grund fuer die vielen ' und .
my $sqlplus = $ENV{ORACLE_HOME}.'/'.'bin'.'/'.'sqlplus';
if ((-x $ENV{ORACLE_HOME}.'/'.'sqlplus') && ( -f $ENV{ORACLE_HOME}.'/'.'sqlplus')) {
$sqlplus = $ENV{ORACLE_HOME}.'/'.'sqlplus';
}
my $tnsping = $ENV{ORACLE_HOME}.'/'.'bin'.'/'.'tnsping';
if (! -x $sqlplus) {
die "nosqlplus\n";
}
if ($self->{mode} =~ /^server::tnsping/) {
if ($self->{loginstring} eq "traditional") {
$self->{sqlplus} = sprintf "%s -S %s/%s@%s < /dev/null",
$sqlplus,
$self->{username}, $self->{password}, $self->{sid};
} elsif ($self->{loginstring} eq "extauth") {
$self->{sqlplus} = sprintf "%s -S / < /dev/null",
$sqlplus;
} elsif ($self->{loginstring} eq "passwordstore") {
$self->{sqlplus} = sprintf "%s -S /@%s < /dev/null",
$sqlplus,
$self->{username};
} elsif ($self->{loginstring} eq "sysdba") {
$self->{sqlplus} = sprintf "%s -S / as sysdba < /dev/null",
$sqlplus;
} elsif ($self->{loginstring} eq "sys") {
$self->{sqlplus} = sprintf "%s -S %s/%s@%s as sysdba < /dev/null",
$sqlplus,
$self->{username}, $self->{password}, $self->{sid};
}
} else {
if ($self->{loginstring} eq "traditional") {
$self->{sqlplus} = sprintf "%s -S %s/%s@%s < %s > %s",
$sqlplus,
$self->{username}, $self->{password}, $self->{sid},
$self->{sql_commandfile}, $self->{sql_resultfile};
} elsif ($self->{loginstring} eq "extauth") {
$self->{sqlplus} = sprintf "%s -S / < %s > %s",
$sqlplus,
$self->{sql_commandfile}, $self->{sql_resultfile};
} elsif ($self->{loginstring} eq "passwordstore") {
$self->{sqlplus} = sprintf "%s -S /@%s < %s > %s",
$sqlplus,
$self->{username},
$self->{sql_commandfile}, $self->{sql_resultfile};
} elsif ($self->{loginstring} eq "sysdba") {
$self->{sqlplus} = sprintf "%s -S / as sysdba < %s > %s",
$sqlplus,
$self->{sql_commandfile}, $self->{sql_resultfile};
} elsif ($self->{loginstring} eq "sys") {
$self->{sqlplus} = sprintf "%s -S %s/%s@%s as sysdba < %s > %s",
$sqlplus,
$self->{username}, $self->{password}, $self->{sid},
$self->{sql_commandfile}, $self->{sql_resultfile};
}
}
use POSIX ':signal_h';
local $SIG{'ALRM'} = sub {
die "alarm\n";
};
my $mask = POSIX::SigSet->new( SIGALRM );
my $action = POSIX::SigAction->new(
sub { die "alarm\n" ; }, $mask);
my $oldaction = POSIX::SigAction->new();
sigaction(SIGALRM ,$action ,$oldaction );
alarm($self->{timeout} - 1); # 1 second before the global unknown timeout
if ($self->{mode} =~ /^server::tnsping/) {
if (-x $tnsping) {
my $exit_output = `$tnsping $self->{sid}`;
if ($?) {
# printf STDERR "tnsping exit bumm \n";
# immer 1 bei misserfolg
}
if ($exit_output =~ /^OK \(\d+/m) {
die "ORA-01017"; # fake a successful connect with wrong password
} elsif ($exit_output =~ /^(TNS\-\d+)/m) {
die $1;
} else {
die "TNS-03505";
}
} else {
my $exit_output = `$self->{sqlplus}`;
if ($?) {
printf STDERR "ping exit bumm \n";
}
$exit_output =~ s/\n//g;
$exit_output =~ s/at $0//g;
chomp $exit_output;
die $exit_output;
}
} else {
my $answer = $self->fetchrow_array(
q{ SELECT 42 FROM dual});
die unless defined $answer and $answer == 42;
}
$retval = $self;
};
if ($@) {
$self->{errstr} = $@;
$self->{errstr} =~ s/at $0 .*//g;
chomp $self->{errstr};
$retval = undef;
}
}
$self->{tac} = Time::HiRes::time();
return $retval;
}
sub fetchrow_array {
my $self = shift;
my $sql = shift;
my @arguments = @_;
my $sth = undef;
my @row = ();
$self->trace(sprintf "fetchrow_array: %s", $sql);
$self->trace(sprintf "args: %s", Data::Dumper::Dumper(\@arguments));
foreach (@arguments) {
# replace the ? by the parameters
if (/^\d+$/) {
$sql =~ s/\?/$_/;
} else {
$sql =~ s/\?/'$_'/;
}
}
$self->create_commandfile($sql);
my $exit_output = `$self->{sqlplus}`;
if ($?) {
printf STDERR "fetchrow_array exit bumm \n";
my $output = do { local (@ARGV, $/) = $self->{sql_resultfile}; <> };
my @oerrs = map {
/(ORA\-\d+:.*)/ ? $1 : ();
} split(/\n/, $output);
$self->{errstr} = join(" ", @oerrs);
} else {
my $output = do { local (@ARGV, $/) = $self->{sql_resultfile}; <> };
@row = map { convert($_) }
map { s/^\s+([\.\d]+)$/$1/g; $_ } # strip leading space from numbers
map { s/\s+$//g; $_ } # strip trailing space
split(/\|/, (split(/\n/, $output))[0]);
}
if ($@) {
$self->debug(sprintf "bumm %s", $@);
}
unlink $self->{sql_commandfile};
unlink $self->{sql_resultfile};
return $row[0] unless wantarray;
return @row;
}
sub fetchall_array {
my $self = shift;
my $sql = shift;
my @arguments = @_;
my $sth = undef;
my $rows = undef;
$self->trace(sprintf "fetchall_array: %s", $sql);
$self->trace(sprintf "args: %s", Data::Dumper::Dumper(\@arguments));
foreach (@arguments) {
# replace the ? by the parameters
if (/^\d+$/) {
$sql =~ s/\?/$_/;
} else {
$sql =~ s/\?/'$_'/;
}
}
$self->create_commandfile($sql);
my $exit_output = `$self->{sqlplus}`;
if ($?) {
printf STDERR "fetchrow_array exit bumm %s\n", $exit_output;
my $output = do { local (@ARGV, $/) = $self->{sql_resultfile}; <> };
my @oerrs = map {
/(ORA\-\d+:.*)/ ? $1 : ();
} split(/\n/, $output);
$self->{errstr} = join(" ", @oerrs);
} else {
my $output = do { local (@ARGV, $/) = $self->{sql_resultfile}; <> };
my @rows = map { [
map { convert($_) }
map { s/^\s+([\.\d]+)$/$1/g; $_ }
map { s/\s+$//g; $_ }
split /\|/
] } grep { ! /^\d+ rows selected/ }
grep { ! /^\d+ [Zz]eilen ausgew / }
grep { ! /^Elapsed: / }
grep { ! /^\s*$/ } split(/\n/, $output);
$rows = \@rows;
}
if ($@) {
$self->debug(sprintf "bumm %s", $@);
}
unlink $self->{sql_commandfile};
unlink $self->{sql_resultfile};
return @{$rows};
}
sub func {
my $self = shift;
my $function = shift;
$self->{handle}->func(@_);
}
sub convert {
my $n = shift;
# mostly used to convert numbers in scientific notation
if ($n =~ /^\s*\d+\s*$/) {
return $n;
} elsif ($n =~ /^\s*([-+]?)(\d*[\.,]*\d*)[eE]{1}([-+]?)(\d+)\s*$/) {
my ($vor, $num, $sign, $exp) = ($1, $2, $3, $4);
$n =~ s/E/e/g;
$n =~ s/,/\./g;
$num =~ s/,/\./g;
my $sig = $sign eq '-' ? "." . ($exp - 1 + length $num) : '';
my $dec = sprintf "%${sig}f", $n;
$dec =~ s/\.[0]+$//g;
return $dec;
} elsif ($n =~ /^\s*([-+]?)(\d+)[\.,]*(\d*)\s*$/) {
return $1.$2.".".$3;
} elsif ($n =~ /^\s*(.*?)\s*$/) {
return $1;
} else {
return $n;
}
}
sub execute {
my $self = shift;
my $sql = shift;
eval {
my $sth = $self->{handle}->prepare($sql);
$sth->execute();
};
if ($@) {
printf STDERR "bumm %s\n", $@;
}
}
sub DESTROY {
my $self = shift;
$self->trace("try to clean up command and result files");
unlink $self->{sql_commandfile} if -f $self->{sql_commandfile};
unlink $self->{sql_resultfile} if -f $self->{sql_resultfile};
}
sub create_commandfile {
my $self = shift;
my $sql = shift;
open CMDCMD, "> $self->{sql_commandfile}";
printf CMDCMD "SET HEADING OFF\n";
printf CMDCMD "SET PAGESIZE 0\n";
printf CMDCMD "SET LINESIZE 32767\n";
printf CMDCMD "SET COLSEP '|'\n";
printf CMDCMD "SET TAB OFF\n";
printf CMDCMD "SET FEEDBACK OFF\n";
printf CMDCMD "SET TRIMSPOOL ON\n";
printf CMDCMD "SET NUMFORMAT 9.999999EEEE\n";
printf CMDCMD "SPOOL %s\n", $self->{sql_resultfile};
# printf CMDCMD "ALTER SESSION SET NLS_NUMERIC_CHARACTERS='.,';\n/\n";
printf CMDCMD "%s\n/\n", $sql;
printf CMDCMD "EXIT\n";
close CMDCMD;
}
package DBD::Oracle::Server::Connection::Sqlrelay;
use strict;
use Net::Ping;
our @ISA = qw(DBD::Oracle::Server::Connection);
sub init {
my $self = shift;
my %params = @_;
my $retval = undef;
if ($self->{mode} =~ /^server::tnsping/) {
if (! $self->{connect}) {
$self->{errstr} = "Please specify a database";
} else {
if ($self->{connect} =~ /([\.\w]+):(\d+)/) {
$self->{host} = $1;
$self->{port} = $2;
$self->{socket} = "";
} elsif ($self->{connect} =~ /([\.\w]+):([\w\/]+)/) {
$self->{host} = $1;
$self->{socket} = $2;
$self->{port} = "";
}
}
} else {
if (! $self->{connect} || ! $self->{username} || ! $self->{password}) {
if ($self->{connect} && $self->{connect} =~ /(\w+)\/(\w+)@([\.\w]+):(\d+)/) {
$self->{username} = $1;
$self->{password} = $2;
$self->{host} = $3;
$self->{port} = $4;
$self->{socket} = "";
} elsif ($self->{connect} && $self->{connect} =~ /(\w+)\/(\w+)@([\.\w]+):([\w\/]+)/) {
$self->{username} = $1;
$self->{password} = $2;
$self->{host} = $3;
$self->{socket} = $4;
$self->{port} = "";
} else {
$self->{errstr} = "Please specify database, username and password";
return undef;
}
} else {
if ($self->{connect} =~ /([\.\w]+):(\d+)/) {
$self->{host} = $1;
$self->{port} = $2;
$self->{socket} = "";
} elsif ($self->{connect} =~ /([\.\w]+):([\w\/]+)/) {
$self->{host} = $1;
$self->{socket} = $2;
$self->{port} = "";
} else {
$self->{errstr} = "Please specify database, username and password";
return undef;
}
}
}
if (! exists $self->{errstr}) {
eval {
require DBI;
use POSIX ':signal_h';
local $SIG{'ALRM'} = sub {
die "alarm\n";
};
my $mask = POSIX::SigSet->new( SIGALRM );
my $action = POSIX::SigAction->new(
sub { die "alarm\n" ; }, $mask);
my $oldaction = POSIX::SigAction->new();
sigaction(SIGALRM ,$action ,$oldaction );
alarm($self->{timeout} - 1); # 1 second before the global unknown timeout
if ($self->{handle} = DBI->connect(
sprintf("DBI:SQLRelay:host=%s;port=%d;socket=%s", $self->{host}, $self->{port}, $self->{socket}),
$self->{username},
$self->{password},
{ RaiseError => 1, AutoCommit => 0, PrintError => 1 })) {
$self->{handle}->do(q{
ALTER SESSION SET NLS_NUMERIC_CHARACTERS=".," });
$retval = $self;
if ($self->{mode} =~ /^server::tnsping/ && $self->{handle}->ping()) {
# database connected. fake a "unknown user"
$self->{errstr} = "ORA-01017";
}
} else {
$self->{errstr} = DBI::errstr();
}
};
if ($@) {
$self->{errstr} = $@;
$self->{errstr} =~ s/at [\w\/\.]+ line \d+.*//g;
$retval = undef;
}
}
$self->{tac} = Time::HiRes::time();
return $retval;
}
sub fetchrow_array {
my $self = shift;
my $sql = shift;
my @arguments = @_;
my $sth = undef;
my @row = ();
$self->trace(sprintf "fetchrow_array: %s", $sql);
$self->trace(sprintf "args: %s", Data::Dumper::Dumper(\@arguments));
eval {
$sth = $self->{handle}->prepare($sql);
if (scalar(@arguments)) {
$sth->execute(@arguments);
} else {
$sth->execute();
}
@row = $sth->fetchrow_array();
};
if ($@) {
$self->debug(sprintf "bumm %s", $@);
}
if (-f "/tmp/check_oracle_health_simulation/".$self->{mode}) {
my $simulation = do { local (@ARGV, $/) =
"/tmp/check_oracle_health_simulation/".$self->{mode}; <> };
@row = split(/\s+/, (split(/\n/, $simulation))[0]);
}
return $row[0] unless wantarray;
return @row;
}
sub fetchall_array {
my $self = shift;
my $sql = shift;
my @arguments = @_;
my $sth = undef;
my $rows = undef;
$self->trace(sprintf "fetchall_array: %s", $sql);
$self->trace(sprintf "args: %s", Data::Dumper::Dumper(\@arguments));
eval {
$sth = $self->{handle}->prepare($sql);
if (scalar(@arguments)) {
$sth->execute(@arguments);
} else {
$sth->execute();
}
$rows = $sth->fetchall_arrayref();
};
if ($@) {
printf STDERR "bumm %s\n", $@;
}
if (-f "/tmp/check_oracle_health_simulation/".$self->{mode}) {
my $simulation = do { local (@ARGV, $/) =
"/tmp/check_oracle_health_simulation/".$self->{mode}; <> };
@{$rows} = map { [ split(/\s+/, $_) ] } split(/\n/, $simulation);
}
return @{$rows};
}
sub func {
my $self = shift;
$self->{handle}->func(@_);
}
sub execute {
my $self = shift;
my $sql = shift;
eval {
my $sth = $self->{handle}->prepare($sql);
$sth->execute();
};
if ($@) {
printf STDERR "bumm %s\n", $@;
}
}
sub DESTROY {
my $self = shift;
#$self->trace(sprintf "disconnecting DBD %s",
# $self->{handle} ? "with handle" : "without handle");
#$self->{handle}->disconnect() if $self->{handle};
}
package main;
use strict;
use Getopt::Long qw(:config no_ignore_case);
use File::Basename;
use lib dirname($0);
use vars qw ($PROGNAME $REVISION $CONTACT $TIMEOUT $STATEFILESDIR $needs_restart %commandline);
$PROGNAME = "check_oracle_health";
$REVISION = '$Revision: 1.6.3 $';
$CONTACT = 'gerhard.lausser@consol.de';
$TIMEOUT = 60;
$STATEFILESDIR = '/var/tmp/check_oracle_health';
$needs_restart = 0;
my @modes = (
['server::tnsping',
'tnsping', undef,
'Check the reachability of the server' ],
['server::connectiontime',
'connection-time', undef,
'Time to connect to the server' ],
['server::instance::connectedusers',
'connected-users', undef,
'Number of currently connected users' ],
['server::instance::blockingsessions',
'blocking-sessions', undef,
'Number of currently blocking-sessions' ],
['server::instance::sessionnum',
'session_num', undef,
'Number of currently session_num' ],
['server::instance::locktimes',
'lock-times', undef,
'Number of currently lock-times' ],
['server::instance::locknumbers',
'lock-numbers', undef,
'Number of currently lock-numbers' ],
['server::instance::longtransactions',
'long-transactions', undef,
'Number of currently long-transactions' ],
['server::instance::usedspaces',
'used-spaces', undef,
'Number of currently used-spaces' ],
['server::instance::sga::databuffer::hitratio',
'sga-data-buffer-hit-ratio', undef,
'Data Buffer Cache Hit Ratio' ],
['server::instance::sga::sharedpool::librarycache::hitratio',
'sga-library-cache-hit-ratio', undef,
'Library Cache Hit Ratio' ],
['server::instance::sga::sharedpool::dictionarycache::hitratio',
'sga-dictionary-cache-hit-ratio', undef,
'Dictionary Cache Hit Ratio' ],
['server::instance::sga::latch::hitratio',
'sga-latches-hit-ratio', undef,
'Latches Hit Ratio' ],
['server::instance::sga::sharedpool::reloads',
'sga-shared-pool-reload-ratio', ['sga-shared-pool-reloads'],
'Shared Pool Reloads vs. Pins' ],
['server::instance::sga::sharedpool::free',
'sga-shared-pool-free', undef,
'Shared Pool Free Memory' ],
['server::instance::pga::inmemorysortratio',
'pga-in-memory-sort-ratio', undef,
'PGA in-memory sort ratio' ],
['server::database::invalidobjects',
'invalid-objects', undef,
'Number of invalid objects in database' ],
['server::database::stalestats',
'stale-statistics', undef,
'Find objects with stale optimizer statistics' ],
['server::database::tablespace::usage',
'tablespace-usage', undef,
'Used space in tablespaces' ],
['server::database::tablespace::free',
'tablespace-free', undef,
'Free space in tablespaces' ],
['server::database::tablespace::remainingfreetime',
'tablespace-remaining-time', undef,
'Remaining time until a tablespace is full' ],
['server::database::tablespace::fragmentation',
'tablespace-fragmentation', undef,
'Free space fragmentation index' ],
['server::database::tablespace::iobalance',
'tablespace-io-balance', undef,
'balanced io of all datafiles' ],
['server::database::tablespace::segment::extendspace',
'tablespace-can-allocate-next', undef,
'Segments (of a tablespace) can allocate next extent' ],
['server::database::tablespace::datafile::iotraffic',
'datafile-io-traffic', undef,
'io operations/per sec of a datafile' ],
['server::instance::sga::sharedpool::softparse',
'soft-parse-ratio', undef,
'Percentage of soft parses' ],
['server::instance::sga::redologbuffer::switchinterval',
'switch-interval', ['redo-switch-interval', 'rac-switch-interval'],
'Time between redo log file switches' ],
['server::instance::sga::redologbuffer::retryratio',
'retry-ratio', ['redo-retry-ratio'],
'Redo buffer allocation retries' ],
['server::instance::sga::redologbuffer::iotraffic',
'redo-io-traffic', undef,
'Redo log io bytes per second' ],
['server::instance::sga::rollbacksegments::headercontention',
'roll-header-contention', undef,
'Rollback segment header contention' ],
['server::instance::sga::rollbacksegments::blockcontention',
'roll-block-contention', undef,
'Rollback segment block contention' ],
['server::instance::sga::rollbacksegments::hitratio',
'roll-hit-ratio', undef,
'Rollback segment hit ratio (gets/waits)' ],
['server::instance::sga::rollbacksegments::wraps',
'roll-wraps', undef,
'Rollback segment wraps (per sec)' ],
['server::instance::sga::rollbacksegments::extends',
'roll-extends', undef,
'Rollback segment extends (per sec)' ],
['server::instance::sga::rollbacksegments::avgactivesize',
'roll-avgactivesize', undef,
'Rollback segment average active size' ],
['server::database::tablespace::segment::top10logicalreads',
'seg-top10-logical-reads', undef,
'user objects among top 10 logical reads' ],
['server::database::tablespace::segment::top10physicalreads',
'seg-top10-physical-reads', undef,
'user objects among top 10 physical reads' ],
['server::database::tablespace::segment::top10bufferbusywaits',
'seg-top10-buffer-busy-waits', undef,
'user objects among top 10 buffer busy waits' ],
['server::database::tablespace::segment::top10rowlockwaits',
'seg-top10-row-lock-waits', undef,
'user objects among top 10 row lock waits' ],
['server::instance::event::waits',
'event-waits', undef,
'processes wait events' ],
['server::instance::event::waiting',
'event-waiting', undef,
'time spent by processes waiting for an event' ],
['server::instance::enqueue::contention',
'enqueue-contention', undef,
'percentage of enqueue requests which must wait' ],
['server::instance::enqueue::waiting',
'enqueue-waiting', undef,
'percentage of time spent waiting for the enqueue' ],
['server::instance::sga::latch::contention',
'latch-contention', undef,
'percentage of latch get requests which must wait' ],
['server::instance::sga::latch::waiting',
'latch-waiting', undef,
'percentage of time a latch spends sleeping' ],
['server::instance::sysstat::rate',
'sysstat', undef,
'change of sysstat values over time' ],
['server::sql',
'sql', undef,
'any sql command returning a single number' ],
['server::database::tablespace::listtablespaces',
'list-tablespaces', undef,
'convenience function which lists all tablespaces' ],
['server::database::tablespace::datafile::listdatafiles',
'list-datafiles', undef,
'convenience function which lists all datafiles' ],
['server::instance::enqueue::listenqueues',
'list-enqueues', undef,
'convenience function which lists all enqueues' ],
['server::instance::sga::latch::listlatches',
'list-latches', undef,
'convenience function which lists all latches' ],
['server::instance::event::listevents',
'list-events', undef,
'convenience function which lists all events' ],
['server::instance::event::listeventsbg',
'list-background-events', undef,
'convenience function which lists all background events' ],
['server::instance::sysstat::listsysstats',
'list-sysstats', undef,
'convenience function which lists all statistics from v$sysstat' ],
);
sub print_usage () {
print <
$PROGNAME [-v] [-t
--username=
--tablespace=
$PROGNAME [-h | --help]
$PROGNAME [-V | --version]
Options:
--connect
the connect string
--username
the oracle user
--password
the oracle user's password
--warning
the warning range
--critical
the critical range
--mode
the mode of the plugin. select one of the following keywords:
EOUS
my $longest = length ((reverse sort {length $a <=> length $b} map { $_->[1] } @modes)[0]);
my $format = " %-".
(length ((reverse sort {length $a <=> length $b} map { $_->[1] } @modes)[0])).
"s\t(%s)\n";
foreach (@modes) {
printf $format, $_->[1], $_->[3];
}
printf "\n";
print <
the name of the tablespace, datafile, wait event,
latch, enqueue, or sql statement depending on the mode.
--name2
if name is a sql statement, this statement would appear in
the output and the performance data. This can be ugly, so
name2 can be used to appear instead.
--regexp
if this parameter is used, name will be interpreted as a
regular expression.
--units
one of %, KB, MB, GB. This is used for a better output of mode=sql
and for specifying thresholds for mode=tablespace-free
Tablespace-related modes check all tablespaces in one run by default.
If only a single tablespace should be checked, use the --name parameter.
The same applies to datafile-related modes.
tablespace-remaining-time will take historical data into account. The number
of days in the past can be given with the --lookback parameter. (Default: 30)
In mode sql you can url-encode the statement so you will not have to mess
around with special characters in your Nagios service definitions.
Instead of
--name="select count(*) from v\$session where status = 'ACTIVE'"
you can say
--name=select%20count%28%2A%29%20from%20v%24session%20where%20status%20%3D%20%27ACTIVE%27
For your convenience you can call check_oracle_health with the --encode
option and it will encode the standard input.
EOUS
#
# --basis
# one of rate, delta, value
}
sub print_help () {
print "Copyright (c) 2008 Gerhard Lausser\n\n";
print "\n";
print " Check various parameters of Oracle databases \n";
print "\n";
print_usage();
support();
}
sub print_revision ($$) {
my $commandName = shift;
my $pluginRevision = shift;
$pluginRevision =~ s/^\$Revision: //;
$pluginRevision =~ s/ \$\s*$//;
print "$commandName ($pluginRevision)\n";
print "This nagios plugin comes with ABSOLUTELY NO WARRANTY. You may redistribute\ncopies of this plugin under the terms of the GNU General Public License.\n";
}
sub support () {
my $support='Send email to gerhard.lausser@consol.de if you have questions\nregarding use of this software. \nPlease include version information with all correspondence (when possible,\nuse output from the --version option of the plugin itself).\n';
$support =~ s/@/\@/g;
$support =~ s/\\n/\n/g;
print $support;
}
sub contact_author ($$) {
my $item = shift;
my $strangepattern = shift;
if ($commandline{verbose}) {
printf STDERR
"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n".
"You found a line which is not recognized by %s\n".
"This means, certain components of your system cannot be checked.\n".
"Please contact the author %s and\nsend him the following output:\n\n".
"%s /%s/\n\nThank you!\n".
"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n",
$PROGNAME, $CONTACT, $item, $strangepattern;
}
}
%commandline = ();
my @params = (
"timeout|t=i",
"version|V",
"help|h",
"verbose|v",
"debug|d",
"connect|c=s",
"username|u=s",
"password|p=s",
"mode|m=s",
"tablespace=s",
"datafile=s",
"waitevent=s",
"name=s",
"name2=s",
"regexp",
"perfdata",
"warning=s",
"critical=s",
"absolute|a",
"basis",
"lookback|l=i",
"environment|e=s%",
"method=s",
"runas|r=s",
"scream",
"shell",
"eyecandy",
"encode",
"units=s",
"3");
if (! GetOptions(\%commandline, @params)) {
print_help();
exit $ERRORS{UNKNOWN};
}
if (exists $commandline{version}) {
print_revision($PROGNAME, $REVISION);
exit $ERRORS{OK};
}
if (exists $commandline{help}) {
print_help();
exit $ERRORS{OK};
} elsif (! exists $commandline{mode}) {
printf "Please select a mode\n";
print_help();
exit $ERRORS{OK};
}
if ($commandline{mode} eq "encode") {
my $input = <>;
chomp $input;
$input =~ s/([^A-Za-z0-9])/sprintf("%%%02X", ord($1))/seg;
printf "%s\n", $input;
exit $ERRORS{OK};
}
if (exists $commandline{3}) {
$ENV{NRPE_MULTILINESUPPORT} = 1;
}
if (exists $commandline{timeout}) {
$TIMEOUT = $commandline{timeout};
}
if (exists $commandline{verbose}) {
$DBD::Oracle::Server::verbose = exists $commandline{verbose};
}
if (exists $commandline{scream}) {
# $DBD::Oracle::Server::hysterical = exists $commandline{scream};
}
if (exists $commandline{method}) {
# dbi, snmp or sqlplus
} else {
$commandline{method} = "dbi";
}
$DBD::Oracle::Server::my_modules_dyn_dir = '/usr/local/nagios/libexec';
if (exists $commandline{environment}) {
# if the desired environment variable values are different from
# the environment of this running script, then a restart is necessary.
# because setting $ENV does _not_ change the environment of the running script.
foreach (keys %{$commandline{environment}}) {
if ((! $ENV{$_}) || ($ENV{$_} ne $commandline{environment}->{$_})) {
$needs_restart = 1;
$ENV{$_} = $commandline{environment}->{$_};
printf STDERR "new %s=%s forces restart\n", $_, $ENV{$_}
if $DBD::Oracle::Server::verbose;
}
}
# e.g. called with --runas dbnagio. shlib_path environment variable is stripped
# during the sudo.
# so the perl interpreter starts without a shlib_path. but --runas cares for
# a --environment shlib_path=...
# so setting the environment variable in the code above and restarting the
# perl interpreter will help it find shared libs
}
if (exists $commandline{runas}) {
# remove the runas parameter
# exec sudo $0 ... the remaining parameters
$needs_restart = 1;
# if the calling script has a path for shared libs and there is no --environment
# parameter then the called script surely needs the variable too.
foreach my $important_env qw(LD_LIBRARY_PATH SHLIB_PATH
ORACLE_HOME TNS_ADMIN ORA_NLS ORA_NLS33 ORA_NLS10) {
if ($ENV{$important_env} && ! scalar(grep { /^$important_env=/ }
keys %{$commandline{environment}})) {
$commandline{environment}->{$important_env} = $ENV{$important_env};
printf STDERR "add important --environment %s=%s\n",
$important_env, $ENV{$important_env} if $DBD::Oracle::Server::verbose;
}
}
}
if ($needs_restart) {
my @newargv = ();
my $runas = undef;
if (exists $commandline{runas}) {
$runas = $commandline{runas};
delete $commandline{runas};
}
foreach my $option (keys %commandline) {
if (grep { /^$option/ && /=/ } @params) {
if (ref ($commandline{$option}) eq "HASH") {
foreach (keys %{$commandline{$option}}) {
push(@newargv, sprintf "--%s", $option);
push(@newargv, sprintf "%s=%s", $_, $commandline{$option}->{$_});
}
} else {
push(@newargv, sprintf "--%s", $option);
push(@newargv, sprintf "%s", $commandline{$option});
}
} else {
push(@newargv, sprintf "--%s", $option);
}
}
if ($runas && ($> == 0)) {
# this was not my idea. some people connect as root to their nagios clients.
exec "su", "-c", sprintf("%s %s", $0, join(" ", @newargv)), "-", $runas;
} elsif ($runas) {
exec "sudo", "-S", "-u", $runas, $0, @newargv;
} else {
exec $0, @newargv;
# this makes sure that even a SHLIB or LD_LIBRARY_PATH are set correctly
# when the perl interpreter starts. Setting them during runtime does not
# help loading e.g. libclntsh.so
}
exit;
}
if (exists $commandline{shell}) {
# forget what you see here.
system("/bin/sh");
}
if (exists $commandline{name}) {
# objects can be encoded like an url
# with s/([^A-Za-z0-9])/sprintf("%%%02X", ord($1))/seg;
if (($commandline{mode} ne "sql") ||
(($commandline{mode} eq "sql") &&
($commandline{name} =~ /select%20/i))) { # protect ... like '%cac%' ... from decoding
$commandline{name} =~ s/\%([A-Fa-f0-9]{2})/pack('C', hex($1))/seg;
}
if ($commandline{name} =~ /^0$/) {
# without this, $params{selectname} would be treated like undef
$commandline{name} = "00";
}
}
$SIG{'ALRM'} = sub {
printf "UNKNOWN - %s timed out after %d seconds\n", $PROGNAME, $TIMEOUT;
exit $ERRORS{UNKNOWN};
};
alarm($TIMEOUT);
my $nagios_level = $ERRORS{UNKNOWN};
my $nagios_message = "";
my $perfdata = "";
my $racmode = 0;
if ($commandline{mode} =~ /^rac-([^\-.]+)/) {
$racmode = 1;
$commandline{mode} =~ s/^rac\-//g;
}
if ($commandline{mode} =~ /^my-([^\-.]+)/) {
my $param = $commandline{mode};
$param =~ s/\-/::/g;
push(@modes, [$param, $commandline{mode}, undef, 'my extension']);
} elsif ((! grep { $commandline{mode} eq $_ } map { $_->[1] } @modes) &&
(! grep { $commandline{mode} eq $_ } map { defined $_->[2] ? @{$_->[2]} : () } @modes)) {
printf "UNKNOWN - mode %s\n", $commandline{mode};
print_usage();
exit 3;
}
my %params = (
timeout => $TIMEOUT,
mode => (
map { $_->[0] }
grep {
($commandline{mode} eq $_->[1]) ||
( defined $_->[2] && grep { $commandline{mode} eq $_ } @{$_->[2]})
} @modes
)[0],
racmode => $racmode,
method => $commandline{method} ||
$ENV{NAGIOS__SERVICEORACLE_METH} ||
$ENV{NAGIOS__HOSTORACLE_METH} || 'dbi',
connect => $commandline{connect} ||
$ENV{NAGIOS__SERVICEORACLE_SID} ||
$ENV{NAGIOS__HOSTORACLE_SID} ||
$ENV{ORACLE_SID},
username => $commandline{username} ||
$ENV{NAGIOS__SERVICEORACLE_USER} ||
$ENV{NAGIOS__HOSTORACLE_USER},
password => $commandline{password} ||
$ENV{NAGIOS__SERVICEORACLE_PASS} ||
$ENV{NAGIOS__HOSTORACLE_PASS},
warningrange => $commandline{warning},
criticalrange => $commandline{critical},
absolute => $commandline{absolute},
lookback => $commandline{lookback},
tablespace => $commandline{tablespace},
datafile => $commandline{datafile},
basis => $commandline{basis},
selectname => $commandline{name} || $commandline{tablespace} || $commandline{datafile},
regexp => $commandline{regexp},
name => $commandline{name},
name2 => $commandline{name2} || $commandline{name},
units => $commandline{units},
eyecandy => $commandline{eyecandy},
statefilesdir => $STATEFILESDIR,
);
my $server = undef;
$server = DBD::Oracle::Server->new(%params);
$server->nagios(%params);
$server->calculate_result();
$nagios_message = $server->{nagios_message};
$nagios_level = $server->{nagios_level};
$perfdata = $server->{perfdata};
printf "%s - %s", $ERRORCODES{$nagios_level}, $nagios_message;
printf " | %s", $perfdata if $perfdata;
printf "\n";
exit $nagios_level;
測試命令:
[root@sznagios libexec]# ./ddcheck_oracle_health --connect=ebpdb --user=dbsnmp --password=dbmon1229db --warning 90 --critical 95 --mode lock-numbers
OK - 0 lock numbers | lock_numbers=0;90;95
[root@sznagios libexec]#
[root@sznagios libexec]#
[root@sznagios libexec]# ./ddcheck_oracle_health --connect=ebpdb --user=dbsnmp --password=dbmon1229db --warning 90 --critical 95 --mode session_num
CRITICAL - 914 session num | session_num=914;90;95
[root@sznagios libexec]#
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29500582/viewspace-1406669/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Check_oracle_health 之阻塞會話數Oracle會話
- SQLServer如何監控阻塞會話SQLServer會話
- oracle會話監控shell指令碼Oracle會話指令碼
- CommMonitor 如何關閉監控會話?會話
- nagios外掛之登陸SBC監控電話數iOS
- Nagios主動監控oracle資料庫check_oracle_healthiOSOracle資料庫
- Check_oracle_health之程式數使用率Oracle
- oracle表空間增加監控Oracle
- 配置CACTI監控MySQL資料庫狀態(5)增加MySQL監控模板MySql資料庫
- MongoDB之監控MongoDB
- 網路螞蟻--會話命令--全過程監控--詳錄 (轉)會話
- 伺服器監控之 ping 監控伺服器
- EMQX Cloud 更新:日誌分析增加更多引數,監控運維更省心MQCloud運維
- 監控並處理一直不釋放資源的會話會話
- 研究發現倉庫數量增加的地方空氣汙染也會隨之增加
- 11.prometheus監控之黑盒(blackbox)監控Prometheus
- ORACLE監控之OSW部署Oracle
- 小花狸監控之MySQLMySql
- 小花狸監控之加密加密
- 小花狸監控之RedisRedis
- 小花狸監控之MongodbMongoDB
- MySQL監控工具之orzdbaMySql
- redis監控之Redis LiveRedis
- Apache Kafka監控之KafkaOffsetMonitorApacheKafka
- ? Hyperf 釋出 v1.1.4 版本,增加 Metric 監控元件元件
- 監控session數量指令碼Session指令碼
- golang 必會之 pprof 監控系列(5) —— cpu 佔用率 統計原理Golang
- 會話技術之 Session會話Session
- 會話技術之Cookie會話Cookie
- 效能測試之Docker監控Docker
- Zabbix監控之遷移Zabbix
- PostgreSQL之鎖監控指令碼SQL指令碼
- springboot之actuator監控Spring Boot
- 監控mysql之orzdba和orztopMySql
- 唯品會輿情監控系統
- TensorFlow學習之會話Sesstion()和互動會話InterativeSesstion()會話
- vertica 設定最大會話數會話
- SQLServer會話數管理(轉貼)SQLServer會話