(plugin) database::mssql - add database state filter option (#3520)

This commit is contained in:
qgarnier 2022-03-02 15:34:07 +01:00 committed by GitHub
parent ea238a2983
commit ddcf440e46
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 216 additions and 121 deletions

View File

@ -24,6 +24,7 @@ use base qw(centreon::plugins::templates::counter);
use strict; use strict;
use warnings; use warnings;
use database::mssql::mode::resources::types qw($database_state);
use centreon::plugins::misc; use centreon::plugins::misc;
use POSIX; use POSIX;
@ -167,9 +168,10 @@ sub new {
bless $self, $class; bless $self, $class;
$options{options}->add_options(arguments => { $options{options}->add_options(arguments => {
'filter-name:s' => { name => 'filter_name' }, 'filter-name:s' => { name => 'filter_name' },
'unit:s' => { name => 'unit', default => 'd' }, 'filter-database-state:s' => { name => 'filter_database_state' },
'full-as-incremental:s' => { name => 'full_as_incremental' } 'unit:s' => { name => 'unit', default => 'd' },
'full-as-incremental:s' => { name => 'full_as_incremental' }
}); });
return $self; return $self;
@ -184,6 +186,34 @@ sub check_options {
} }
} }
sub get_database_state {
my ($self, %options) = @_;
if ($options{sql}->is_version_minimum(version => '9.x')) {
return $database_state->{ $options{state} };
}
my @states = ();
push @states, 'autoclose' if ($options{state} & 1);
push @states, 'select into/bulkcopy' if ($options{state} & 4);
push @states, 'trunc. log on chkpt' if ($options{state} & 8);
push @states, 'torn page detection' if ($options{state} & 16);
push @states, 'loading' if ($options{state} & 32);
push @states, 'pre recovery' if ($options{state} & 64);
push @states, 'recovering' if ($options{state} & 128);
push @states, 'not recovered' if ($options{state} & 256);
push @states, 'offline' if ($options{state} & 512);
push @states, 'read only' if ($options{state} & 1024);
push @states, 'dbo use only' if ($options{state} & 2048);
push @states, 'single user' if ($options{state} & 4096);
push @states, 'emergency mode' if ($options{state} & 32768);
push @states, 'online' if ($options{state} & 65536);
push @states, 'autoshrink' if ($options{state} & 4194304);
push @states, 'cleanly shutdown' if ($options{state} & 1073741824);
return join(',', @states);
}
sub manage_selection { sub manage_selection {
my ($self, %options) = @_; my ($self, %options) = @_;
@ -192,6 +222,7 @@ sub manage_selection {
my $query = q{ my $query = q{
SELECT SELECT
a.name, a.name,
a.status,
a.recovery_model, 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)),
@ -205,6 +236,7 @@ sub manage_selection {
$query = q{ $query = q{
SELECT SELECT
D.name AS [database_name], D.name AS [database_name],
D.state,
D.recovery_model, D.recovery_model,
BS1.last_backup, BS1.last_backup,
BS1.last_duration, BS1.last_duration,
@ -231,8 +263,13 @@ sub manage_selection {
my $map_type = { D => 'full', I => 'incremental', L => 'log' }; my $map_type = { D => 'full', I => 'incremental', L => 'log' };
$self->{databases} = {}; $self->{databases} = {};
foreach my $row (@$result) { foreach my $row (@$result) {
my $state = $self->get_database_state(state => $row->[1], sql => $options{sql});
next if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' && next if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' &&
$row->[0] !~ /$self->{option_results}->{filter_name}/); $row->[0] !~ /$self->{option_results}->{filter_name}/);
next if (defined($self->{option_results}->{filter_database_state}) && $self->{option_results}->{filter_database_state} ne '' &&
$state !~ /$self->{option_results}->{filter_database_state}/);
if (!defined($self->{databases}->{ $row->[0] })) { if (!defined($self->{databases}->{ $row->[0] })) {
$self->{databases}->{ $row->[0] } = { $self->{databases}->{ $row->[0] } = {
all => { exec_seconds => -1, exec_human => '', name => $row->[0] }, all => { exec_seconds => -1, exec_human => '', name => $row->[0] },
@ -242,35 +279,35 @@ sub manage_selection {
}; };
} }
next if (!defined($map_type->{ $row->[4] })); next if (!defined($map_type->{ $row->[5] }));
if ($row->[4] =~ /D|I/) { if ($row->[5] =~ /D|I/) {
if (defined($row->[2]) && ( if (defined($row->[3]) && (
$self->{databases}->{ $row->[0] }->{all}->{exec_seconds} == -1 || $self->{databases}->{ $row->[0] }->{all}->{exec_seconds} == -1 ||
$self->{databases}->{ $row->[0] }->{all}->{exec_seconds} > $row->[2])) { $self->{databases}->{ $row->[0] }->{all}->{exec_seconds} > $row->[3])) {
$self->{databases}->{ $row->[0] }->{all}->{exec_seconds} = $row->[2]; $self->{databases}->{ $row->[0] }->{all}->{exec_seconds} = $row->[3];
$self->{databases}->{ $row->[0] }->{all}->{exec_human} = centreon::plugins::misc::change_seconds( $self->{databases}->{ $row->[0] }->{all}->{exec_human} = centreon::plugins::misc::change_seconds(
value => $row->[2] value => $row->[3]
); );
if (defined($row->[3])) { if (defined($row->[4])) {
$self->{databases}->{ $row->[0] }->{all}->{duration_seconds} = $row->[3]; $self->{databases}->{ $row->[0] }->{all}->{duration_seconds} = $row->[4];
$self->{databases}->{ $row->[0] }->{all}->{duration_human} = centreon::plugins::misc::change_seconds( $self->{databases}->{ $row->[0] }->{all}->{duration_human} = centreon::plugins::misc::change_seconds(
value => $row->[3] value => $row->[4]
); );
} }
} }
} }
if (defined($row->[2])) { if (defined($row->[3])) {
$self->{databases}->{ $row->[0] }->{ $map_type->{ $row->[4] } }->{exec_seconds} = $row->[2]; $self->{databases}->{ $row->[0] }->{ $map_type->{ $row->[5] } }->{exec_seconds} = $row->[3];
$self->{databases}->{ $row->[0] }->{ $map_type->{ $row->[4] } }->{exec_human} = centreon::plugins::misc::change_seconds( $self->{databases}->{ $row->[0] }->{ $map_type->{ $row->[5] } }->{exec_human} = centreon::plugins::misc::change_seconds(
value => $row->[2] value => $row->[3]
); );
} }
if (defined($row->[3])) { if (defined($row->[4])) {
$self->{databases}->{ $row->[0] }->{ $map_type->{ $row->[4] } }->{duration_seconds} = $row->[3]; $self->{databases}->{ $row->[0] }->{ $map_type->{ $row->[5] } }->{duration_seconds} = $row->[4];
$self->{databases}->{ $row->[0] }->{ $map_type->{ $row->[4] } }->{duration_human} = centreon::plugins::misc::change_seconds( $self->{databases}->{ $row->[0] }->{ $map_type->{ $row->[5] } }->{duration_human} = centreon::plugins::misc::change_seconds(
value => $row->[3] value => $row->[4]
); );
} }
} }
@ -303,6 +340,10 @@ Check MSSQL backup.
Filter databases by name. Filter databases by name.
=item B<--filter-database-state>
Filter databases by state.
=item B<--full-as-incremental> =item B<--full-as-incremental>
Last incremental backup time uses last full backup time only if full is newer than incremental. Last incremental backup time uses last full backup time only if full is newer than incremental.

View File

@ -24,6 +24,7 @@ use base qw(centreon::plugins::templates::counter);
use strict; use strict;
use warnings; use warnings;
use database::mssql::mode::resources::types qw($database_state);
sub custom_space_usage_perfdata { sub custom_space_usage_perfdata {
my ($self, %options) = @_; my ($self, %options) = @_;
@ -233,6 +234,7 @@ sub new {
$options{options}->add_options(arguments => { $options{options}->add_options(arguments => {
'filter-database:s' => { name => 'filter_database' }, 'filter-database:s' => { name => 'filter_database' },
'filter-database-state:s' => { name => 'filter_database_state' },
'datafiles-maxsize:s' => { name => 'datafiles_maxsize' }, 'datafiles-maxsize:s' => { name => 'datafiles_maxsize' },
'logfiles-maxsize:s' => { name => 'logfiles_maxsize' }, 'logfiles-maxsize:s' => { name => 'logfiles_maxsize' },
'datafiles-maxsize-unlimited:s' => { name => 'datafiles_maxsize_unlimited' }, 'datafiles-maxsize-unlimited:s' => { name => 'datafiles_maxsize_unlimited' },
@ -259,51 +261,59 @@ sub manage_selection {
} }
} }
$options{sql}->query(query => qq{ $options{sql}->query(query => q{
EXEC sp_MSforeachdb 'USE [?]
SELECT SELECT
DB_NAME(), D.name AS [database_name],
[name], D.state
physical_name, FROM sys.databases D
[File_Type] = CASE type
WHEN 0 THEN ''data''
WHEN 1 THEN ''log''
END,
[Total_Size] = [size],
[Used_Space] = (CAST(FILEPROPERTY([name], ''SpaceUsed'') as int)),
[Growth_Units] = CASE [is_percent_growth]
WHEN 1 THEN CAST(growth AS varchar(20)) + ''%''
ELSE CAST(growth*8/1024 AS varchar(20)) + ''Mb''
END,
[max_size]
FROM sys.database_files'
}); });
my $databases = $options{sql}->fetchall_arrayref();
# limit can be: 'unlimited', 'overload', 'other'. # limit can be: 'unlimited', 'overload', 'other'.
$self->{databases} = {}; $self->{databases} = {};
while ($result = $options{sql}->fetchall_arrayref()) { foreach my $database (@$databases) {
last if (scalar(@$result) <= 0); my $dbname = $database->[0];
next if (defined($self->{option_results}->{filter_database}) && $self->{option_results}->{filter_database} ne '' &&
$dbname !~ /$self->{option_results}->{filter_database}/i);
next if (defined($self->{option_results}->{filter_database_state}) && $self->{option_results}->{filter_database_state} ne '' &&
$database_state->{ $database->[1] } !~ /$self->{option_results}->{filter_database_state}/);
foreach my $row (@$result) { $options{sql}->query(query => qq{
next if (!defined($row->[7])); USE [$dbname]
SELECT
[name],
physical_name,
[File_Type] = CASE type
WHEN 0 THEN ''data''
WHEN 1 THEN ''log''
END,
[Total_Size] = [size],
[Used_Space] = (CAST(FILEPROPERTY([name], ''SpaceUsed'') as int)),
[Growth_Units] = CASE [is_percent_growth]
WHEN 1 THEN CAST(growth AS varchar(20)) + ''%''
ELSE CAST(growth*8/1024 AS varchar(20)) + ''Mb''
END,
[max_size]
FROM sys.database_files
});
if (defined($self->{option_results}->{filter_database}) && $self->{option_results}->{filter_database} ne '' && my $rows = $options{sql}->fetchall_arrayref();
$row->[0] !~ /$self->{option_results}->{filter_database}/i) {
$self->{output}->output_add(debug => 1, long_msg => "skipping database " . $row->[0] . ": no matching filter.");
next;
}
if (!defined($self->{databases}->{ $row->[0] })) { foreach my $row (@$rows) {
$self->{databases}->{ $row->[0] } = { next if (!defined($row->[6]));
name => $row->[0],
if (!defined($self->{databases}->{$dbname})) {
$self->{databases}->{$dbname} = {
name => $dbname,
datafiles => { datafiles => {
name => $row->[0], name => $dbname,
used_space => 0, used_space => 0,
total_space => 0, total_space => 0,
limit => 'other' limit => 'other'
}, },
logfiles => { logfiles => {
name => $row->[0], name => $dbname,
used_space => 0, used_space => 0,
total_space => 0, total_space => 0,
limit => 'other' limit => 'other'
@ -311,30 +321,30 @@ sub manage_selection {
}; };
} }
$self->{databases}->{ $row->[0] }->{$row->[3] . 'files'}->{used_space} += ($row->[5] * 8 * 1024); $self->{databases}->{$dbname}->{$row->[2] . 'files'}->{used_space} += ($row->[4] * 8 * 1024);
my $size = $row->[4] * 8 * 1024; my $size = $row->[3] * 8 * 1024;
#max_size = -1 (=unlimited) #max_size = -1 (=unlimited)
if ($row->[7] == -1) { if ($row->[6] == -1) {
$self->{databases}->{ $row->[0] }->{$row->[3] . 'files'}->{limit} = 'unlimited'; $self->{databases}->{$dbname}->{$row->[2] . 'files'}->{limit} = 'unlimited';
} }
if (defined($self->{option_results}->{check_underlying_disk})) { if (defined($self->{option_results}->{check_underlying_disk})) {
# look for the drives # look for the drives
foreach my $drive_name (keys %$drives) { foreach my $drive_name (keys %$drives) {
if ($row->[2] =~ /^$drive_name/) { if ($row->[1] =~ /^$drive_name/) {
if (($row->[7] > 0) && (($row->[7] * 8 * 1024) <= ($size + $drives->{$drive_name}))) { if (($row->[6] > 0) && (($row->[6] * 8 * 1024) <= ($size + $drives->{$drive_name}))) {
$size = $row->[7] * 8 * 1024; $size = $row->[6] * 8 * 1024;
} elsif (!defined($unlimited_disk->{ $row->[0] . '_' . $row->[3] . 'files_' . $drive_name })) { } elsif (!defined($unlimited_disk->{ $dbname . '_' . $row->[2] . 'files_' . $drive_name })) {
$size += $drives->{$drive_name}; $size += $drives->{$drive_name};
$unlimited_disk->{ $row->[0] . '_' . $row->[3] . 'files_' . $drive_name } = 1; $unlimited_disk->{ $dbname . '_' . $row->[2] . 'files_' . $drive_name } = 1;
} }
last; last;
} }
} }
} elsif ($row->[7] > 0) { } elsif ($row->[6] > 0) {
$size = $row->[7] * 8 * 1024; $size = $row->[6] * 8 * 1024;
} }
$self->{databases}->{ $row->[0] }->{$row->[3] . 'files'}->{total_space} += $size; $self->{databases}->{$dbname}->{ $row->[2] . 'files' }->{total_space} += $size;
} }
} }
@ -375,6 +385,10 @@ Check database data and log files.
Filter database by name (Can be a regex). Filter database by name (Can be a regex).
=item B<--filter-database-state>
Filter databases by state.
=item B<--datafiles-maxsize> =item B<--datafiles-maxsize>
Overload all data files max size (in MB). Overload all data files max size (in MB).

View File

@ -24,6 +24,7 @@ use base qw(centreon::plugins::mode);
use strict; use strict;
use warnings; use warnings;
use database::mssql::mode::resources::types qw($database_state);
sub new { sub new {
my ($class, %options) = @_; my ($class, %options) = @_;
@ -45,41 +46,47 @@ sub check_options {
sub manage_selection { sub manage_selection {
my ($self, %options) = @_; my ($self, %options) = @_;
$self->{sql} = $options{sql}; $options{sql}->connect();
$self->{sql}->connect(); $options{sql}->query(query => q{
$self->{sql}->query(query => q{DBCC SQLPERF(LOGSPACE)}); SELECT
D.name AS [database_name],
D.state
FROM sys.databases D
});
my $result = $self->{sql}->fetchall_arrayref(); my $result = $self->{sql}->fetchall_arrayref();
my $databases = {};
foreach (@$result) {
next if (defined($self->{option_results}->{filter_database}) && $self->{option_results}->{filter_database} ne '' &&
$_->[0] !~ /$self->{option_results}->{filter_database}/);
foreach my $database (@$result) { $databases->{ $_->[0] } = {
if (defined($self->{option_results}->{filter_database}) && $self->{option_results}->{filter_database} ne '' && name => $_->[0],
$$database[0] !~ /$self->{option_results}->{filter_database}/i) { state => $database_state->{ $_->[1] }
next; };
}
$self->{sql}->query(query => "use [" . $$database[0] . "]; exec sp_spaceused;");
my $result2 = $self->{sql}->fetchall_arrayref();
foreach my $row (@$result2) {
$self->{databases}->{$$row[0]} = {
display => $$row[0],
total => convert_bytes($$row[1]),
};
}
} }
return $databases;
} }
sub run { sub run {
my ($self, %options) = @_; my ($self, %options) = @_;
$self->manage_selection(%options); my $databases = $self->manage_selection(%options);
foreach my $database (sort keys %{$self->{databases}}) { foreach (values %$databases) {
$self->{output}->output_add(long_msg => sprintf("[name = %s] [total = %s]", $self->{output}->output_add(
$self->{databases}->{$database}->{display}, $self->{databases}->{$database}->{total})); long_msg => sprintf(
'[name: %s] [state: %s]',
$_->{name},
$_->{state}
)
);
} }
$self->{output}->output_add(severity => 'OK', $self->{output}->output_add(
short_msg => 'List databases:'); severity => 'OK',
short_msg => 'List databases:'
);
$self->{output}->display(nolabel => 1, force_ignore_perfdata => 1, force_long_output => 1); $self->{output}->display(nolabel => 1, force_ignore_perfdata => 1, force_long_output => 1);
$self->{output}->exit(); $self->{output}->exit();
} }
@ -87,36 +94,18 @@ sub run {
sub disco_format { sub disco_format {
my ($self, %options) = @_; my ($self, %options) = @_;
$self->{output}->add_disco_format(elements => ['name', 'total']); $self->{output}->add_disco_format(elements => ['name', 'state']);
} }
sub disco_show { sub disco_show {
my ($self, %options) = @_; my ($self, %options) = @_;
$self->manage_selection(%options); my $databases = $self->manage_selection(%options);
foreach my $database (sort keys %{$self->{databases}}) { foreach (values %$databases) {
$self->{output}->add_disco_entry( $self->{output}->add_disco_entry(%$_);
name => $self->{databases}->{$database}->{display},
total => $self->{databases}->{$database}->{total},
);
} }
} }
sub convert_bytes {
my ($brut) = @_;
my ($value,$unit) = split(/\s+/,$brut);
if ($unit =~ /kb*/i) {
$value = $value * 1024;
} elsif ($unit =~ /mb*/i) {
$value = $value * 1024 * 1024;
} elsif ($unit =~ /gb*/i) {
$value = $value * 1024 * 1024 * 1024;
} elsif ($unit =~ /tb*/i) {
$value = $value * 1024 * 1024 * 1024 * 1024;
}
return $value;
}
1; 1;
__END__ __END__

View File

@ -0,0 +1,44 @@
#
# Copyright 2022 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package database::mssql::mode::resources::types;
use strict;
use warnings;
use Exporter;
our $database_state;
our @ISA = qw(Exporter);
our @EXPORT_OK = qw($database_state);
$database_state = {
0 => 'online',
1 => 'restoring',
2 => 'recovering',
3 => 'recoveringPending',
4 => 'suspect',
5 => 'emergency',
6 => 'offline',
7 => 'copying',
10 => 'offlineSecondary'
};
1;

View File

@ -24,6 +24,7 @@ use base qw(centreon::plugins::templates::counter);
use strict; use strict;
use warnings; use warnings;
use database::mssql::mode::resources::types qw($database_state);
sub prefix_database_output { sub prefix_database_output {
my ($self, %options) = @_; my ($self, %options) = @_;
@ -114,8 +115,9 @@ sub new {
bless $self, $class; bless $self, $class;
$options{options}->add_options(arguments => { $options{options}->add_options(arguments => {
'filter-database:s' => { name => 'filter_database' }, 'filter-database:s' => { name => 'filter_database' },
'filter-table:s' => { name => 'filter_table' } 'filter-database-state:s' => { name => 'filter_database_state' },
'filter-table:s' => { name => 'filter_table' }
}); });
return $self; return $self;
@ -165,19 +167,20 @@ sub manage_selection {
my ($self, %options) = @_; my ($self, %options) = @_;
$options{sql}->connect(); $options{sql}->connect();
$options{sql}->query(query => qq{ $options{sql}->query(query => q{
SELECT [name] as database_name SELECT
FROM sys.databases D.name AS [database_name],
D.state
FROM sys.databases D
}); });
$self->{databases} = {}; $self->{databases} = {};
my $results = $options{sql}->fetchall_arrayref(); my $results = $options{sql}->fetchall_arrayref();
foreach my $row (@$results) { foreach my $row (@$results) {
if (defined($self->{option_results}->{filter_database}) && $self->{option_results}->{filter_database} ne '' && next if (defined($self->{option_results}->{filter_database}) && $self->{option_results}->{filter_database} ne '' &&
$row->[0] !~ /$self->{option_results}->{filter_database}/) { $row->[0] !~ /$self->{option_results}->{filter_database}/);
$self->{output}->output_add(long_msg => "skipping '" . $row->[0] . "': no matching filter.", debug => 1); next if (defined($self->{option_results}->{filter_database_state}) && $self->{option_results}->{filter_database_state} ne '' &&
next $database_state->{ $row->[1] } !~ /$self->{option_results}->{filter_database_state}/);
}
$self->{databases}->{ $row->[0] } = { $self->{databases}->{ $row->[0] } = {
display => $row->[0], display => $row->[0],
@ -208,6 +211,10 @@ Check tables size.
Filter tables by database name (Can be a regexp). Filter tables by database name (Can be a regexp).
=item B<--filter-database-state>
Filter databases by state.
=item B<--filter-table> =item B<--filter-table>
Filter tables by name (can be a regexp). Filter tables by name (can be a regexp).