break(mssql): mode backup-age - manage full/incremental/log backup type (#3333)

This commit is contained in:
qgarnier 2021-12-22 13:12:00 +01:00 committed by GitHub
parent bc45db33fc
commit ff91f0b772
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 193 additions and 111 deletions

View File

@ -20,25 +20,127 @@
package database::mssql::mode::backupage; package database::mssql::mode::backupage;
use base qw(centreon::plugins::mode); use base qw(centreon::plugins::templates::counter);
use strict; use strict;
use warnings; use warnings;
use Time::HiRes; use centreon::plugins::misc;
use Time::Local; use POSIX;
my $unitdiv = { s => 1, w => 604800, d => 86400, h => 3600, m => 60 };
my $unitdiv_long = { s => 'seconds', w => 'weeks', d => 'days', h => 'hours', m => 'minutes' };
sub custom_duration_perfdata {
my ($self, %options) = @_;
$self->{output}->perfdata_add(
nlabel => $self->{nlabel},
unit => 's',
instances => [$self->{result_values}->{name}, $self->{result_values}->{type}],
value => $self->{result_values}->{duration_seconds},
warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}),
critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}),
min => 0
);
}
sub custom_backup_perfdata {
my ($self, %options) = @_;
$self->{output}->perfdata_add(
nlabel => $self->{nlabel} . '.' . $unitdiv_long->{ $self->{instance_mode}->{option_results}->{unit} },
unit => $self->{instance_mode}->{option_results}->{unit},
instances => [$self->{result_values}->{name}, $self->{result_values}->{type}],
value => floor($self->{result_values}->{exec_seconds} / $unitdiv->{ $self->{instance_mode}->{option_results}->{unit} }),
warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}),
critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}),
min => -1
);
}
sub custom_backup_threshold {
my ($self, %options) = @_;
return $self->{perfdata}->threshold_check(
value => floor($self->{result_values}->{exec_seconds} / $unitdiv->{ $self->{instance_mode}->{option_results}->{unit} }),
threshold => [
{ label => 'critical-' . $self->{thlabel}, exit_litteral => 'critical' },
{ label => 'warning-'. $self->{thlabel}, exit_litteral => 'warning' },
{ label => 'unknown-'. $self->{thlabel}, exit_litteral => 'unknown' }
]
);
}
sub custom_backup_output {
my ($self, %options) = @_;
my $msg;
if ($self->{result_values}->{exec_seconds} == -1) {
$msg = 'backup never executed';
} else {
$msg = 'last backup execution: ' . $self->{result_values}->{exec_human};
}
return $msg;
}
sub prefix_database_output {
my ($self, %options) = @_;
return "database '" . $options{instance} . "' ";
}
sub database_long_output {
my ($self, %options) = @_;
return "checking database '" . $options{instance} . "'";
}
sub prefix_full_output { return 'full '; }
sub prefix_incremental_output { return 'incremental '; }
sub prefix_log_output { return 'log '; }
sub set_counters {
my ($self, %options) = @_;
$self->{maps_counters_type} = [
{
name => 'databases', type => 3, cb_prefix_output => 'prefix_database_output', cb_long_output => 'database_long_output', indent_long_output => ' ', message_multiple => 'All databases are ok',
group => [
{ name => 'full', type => 0, cb_prefix_output => 'prefix_full_output', skipped_code => { -10 => 1 } },
{ name => 'incremental', type => 0, cb_prefix_output => 'prefix_incremental_output', skipped_code => { -10 => 1 } },
{ name => 'log', type => 0, cb_prefix_output => 'prefix_log_output', skipped_code => { -10 => 1 } }
]
}
];
foreach ('full', 'incremental', 'log') {
$self->{maps_counters}->{$_} = [
{ label => $_ . '-last-execution', nlabel => 'backup.time.last.execution', set => {
key_values => [ { name => 'exec_seconds' }, { name => 'exec_human' }, { name => 'name' }, { name => 'type' } ],
closure_custom_output => $self->can('custom_backup_output'),
closure_custom_perfdata => $self->can('custom_backup_perfdata'),
closure_custom_threshold_check => $self->can('custom_backup_threshold')
}
},
{ label => $_ . '-last-duration', nlabel => 'backup.time.last.duration.seconds', set => {
key_values => [ { name => 'duration_seconds' }, { name => 'duration_human' }, { name => 'name' }, { name => 'type' } ],
output_template => 'duration time: %s',
output_use => 'duration_human',
closure_custom_perfdata => $self->can('custom_duration_perfdata')
}
}
];
}
}
sub new { sub new {
my ($class, %options) = @_; my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options); my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1);
bless $self, $class; bless $self, $class;
$options{options}->add_options(arguments => $options{options}->add_options(arguments => {
{ 'filter-name:s' => { name => 'filter_name' },
"filter:s" => { name => 'filter', }, 'unit:s' => { name => 'unit', default => 'd' }
"skip" => { name => 'skip', },
"skip-no-backup" => { name => 'skip_no_backup', },
"warning:s" => { name => 'warning', },
"critical:s" => { name => 'critical', },
}); });
return $self; return $self;
@ -46,100 +148,84 @@ sub new {
sub check_options { sub check_options {
my ($self, %options) = @_; my ($self, %options) = @_;
$self->SUPER::init(%options); $self->SUPER::check_options(%options);
if (($self->{perfdata}->threshold_validate(label => 'warning', value => $self->{option_results}->{warning})) == 0) { if ($self->{option_results}->{unit} eq '' || !defined($unitdiv->{$self->{option_results}->{unit}})) {
$self->{output}->add_option_msg(short_msg => "Wrong warning threshold '" . $self->{option_results}->{warning} . "'."); $self->{option_results}->{unit} = 'd';
$self->{output}->option_exit();
}
if (($self->{perfdata}->threshold_validate(label => 'critical', value => $self->{option_results}->{critical})) == 0) {
$self->{output}->add_option_msg(short_msg => "Wrong critical threshold '" . $self->{option_results}->{critical} . "'.");
$self->{output}->option_exit();
} }
} }
sub run { sub manage_selection {
my ($self, %options) = @_; my ($self, %options) = @_;
# $options{sql} = sqlmode object
$self->{sql} = $options{sql};
$self->{output}->output_add(severity => 'OK', my $query = q{
short_msg => "All backups are ok."); SELECT
a.name,
$self->{sql}->connect(); a.recovery_model,
my $count = 0;
my $query = 'SELECT
a.name, a.recovery_model,
DATEDIFF(SS, MAX(b.backup_finish_date), GETDATE()), DATEDIFF(SS, MAX(b.backup_finish_date), GETDATE()),
DATEDIFF(SS, MAX(b.backup_start_date), MAX(b.backup_finish_date)) DATEDIFF(SS, MAX(b.backup_start_date), MAX(b.backup_finish_date)),
FROM master.dbo.sysdatabases a LEFT OUTER JOIN msdb.dbo.backupset b b.type
ON b.database_name = a.name FROM master.dbo.sysdatabases a LEFT OUTER JOIN msdb.dbo.backupset b ON b.database_name = a.name
GROUP BY a.name WHERE b.type IN ('D', 'I', 'L')
GROUP BY a.name, b.type
ORDER BY a.name ORDER BY a.name
'; };
if ($options{sql}->is_version_minimum(version => '9.x')) {
if (($self->{sql}->is_version_minimum(version => '9.x'))) { $query = q{
$query = "SELECT D.name AS [database_name], D.recovery_model, BS1.last_backup, BS1.last_duration SELECT
D.name AS [database_name],
D.recovery_model,
BS1.last_backup,
BS1.last_duration,
BS1.type
FROM sys.databases D FROM sys.databases D
LEFT JOIN ( LEFT JOIN (
SELECT BS.[database_name], SELECT
DATEDIFF(SS,MAX(BS.[backup_finish_date]),GETDATE()) AS last_backup, BS.[database_name],
DATEDIFF(SS,MAX(BS.[backup_start_date]),MAX(BS.[backup_finish_date])) AS last_duration BS.[type],
DATEDIFF(SS, MAX(BS.[backup_finish_date]), GETDATE()) AS last_backup,
DATEDIFF(SS, MAX(BS.[backup_start_date]), MAX(BS.[backup_finish_date])) AS last_duration
FROM msdb.dbo.backupset BS FROM msdb.dbo.backupset BS
WHERE BS.type = 'D' WHERE BS.type IN ('D', 'I', 'L')
GROUP BY BS.[database_name] GROUP BY BS.[database_name], BS.[type]
) BS1 ON D.name = BS1.[database_name] ) BS1 ON D.name = BS1.[database_name]
WHERE D.source_database_id IS NULL WHERE D.source_database_id IS NULL
ORDER BY D.[name]"; ORDER BY D.[name]
};
} }
$self->{sql}->query(query => $query); $options{sql}->connect();
my $result = $self->{sql}->fetchall_arrayref(); $options{sql}->query(query => $query);
my $result = $options{sql}->fetchall_arrayref();
my $map_type = { D => 'full', I => 'incremental', L => 'log' };
$self->{databases} = {};
foreach my $row (@$result) { foreach my $row (@$result) {
next if (defined($self->{option_results}->{filter}) && $$row[0] !~ /$self->{option_results}->{filter}/); next if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' &&
$count++; $row->[0] !~ /$self->{option_results}->{filter_name}/);
#dbt_backup_start: 0x1686303d8 (dtdays=40599, dttime=7316475) Feb 27 2011 6:46:28:250AM if (!defined($self->{databases}->{ $row->[0] })) {
my $last_backup = $$row[2]; $self->{databases}->{ $row->[0] } = {
my $backup_duration = $$row[3]; full => { exec_seconds => -1, exec_human => '', type => 'full', name => $row->[0] },
if (!defined($last_backup)) { incremental => { exec_seconds => -1, exec_human => '', type => 'incremental', name => $row->[0] },
if (!defined($self->{option_results}->{skip_no_backup})) { log => { exec_seconds => -1, exec_human => '', type => 'log', name => $row->[0] }
$self->{output}->output_add(severity => 'CRITICAL', };
short_msg => sprintf("No backup found for DB '%s'", $$row[0]));
}
} else {
$self->{output}->output_add(long_msg => sprintf("DB '%s' backup age : %ds [Duration : %ds]", $$row[0], $last_backup, $backup_duration));
my $exit_code = $self->{perfdata}->threshold_check(value => $last_backup, threshold => [ { label => 'critical', exit_litteral => 'critical' }, { label => 'warning', exit_litteral => 'warning' } ]);
if (!$self->{output}->is_status(value => $exit_code, compare => 'ok', litteral => 1)) {
$self->{output}->output_add(severity => $exit_code,
short_msg => sprintf("DB '%s' backup age: %ds [Duration : %ds]", $$row[0], $last_backup, $backup_duration));
} }
$self->{output}->perfdata_add(label => sprintf("db_%s_backup_age",$$row[0]), next if (!defined($map_type->{ $row->[4] }));
unit => 's',
value => $last_backup,
warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning'),
critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical'),
min => 0);
$self->{output}->perfdata_add(label => sprintf("db_%s_backup_duration",$$row[0]),
unit => 's',
value => $backup_duration,
min => 0);
}
}
if ($count == 0) { if (defined($row->[2])) {
$self->{output}->output_add(severity => 'OK', $self->{databases}->{ $row->[0] }->{ $map_type->{ $row->[4] } }->{exec_seconds} = $row->[2];
short_msg => "No backup found"); $self->{databases}->{ $row->[0] }->{ $map_type->{ $row->[4] } }->{exec_human} = centreon::plugins::misc::change_seconds(
if (!defined($self->{option_results}->{skip})) { value => $row->[2]
$self->{output}->output_add(severity => 'UNKNOWN', );
short_msg => "No backup found"); }
if (defined($row->[3])) {
$self->{databases}->{ $row->[0] }->{ $map_type->{ $row->[4] } }->{duration_seconds} = $row->[3];
$self->{databases}->{ $row->[0] }->{ $map_type->{ $row->[4] } }->{duration_human} = centreon::plugins::misc::change_seconds(
value => $row->[3]
);
} }
} }
$self->{output}->display();
$self->{output}->exit();
} }
1; 1;
@ -148,29 +234,25 @@ __END__
=head1 MODE =head1 MODE
Check MSSQL backup age. Check MSSQL backup.
=over 8 =over 8
=item B<--filter> =item B<--filter-name>
Filter database. Filter databases by name.
=item B<--skip> =item B<--unit>
Return ok if no backup found. Select the unit for expires threshold. May be 's' for seconds, 'm' for minutes,
'h' for hours, 'd' for days, 'w' for weeks. Default is days.
=item B<--skip-no-backup> =item B<--warning-*> B<--critical-*>
Skip databases without backup. Thresholds.
Can be: 'incremental-last-execution', 'incremental-last-duration',
=item B<--warning> 'full-last-execution', 'full-last-duration',
'log-last-execution', 'log-last-duration'.
Threshold warning in seconds.
=item B<--critical>
Threshold critical in seconds.
=back =back