diff --git a/database/mssql/mode/backupage.pm b/database/mssql/mode/backupage.pm index 6249d8e61..a7dbc248e 100644 --- a/database/mssql/mode/backupage.pm +++ b/database/mssql/mode/backupage.pm @@ -24,6 +24,7 @@ use base qw(centreon::plugins::templates::counter); use strict; use warnings; +use database::mssql::mode::resources::types qw($database_state); use centreon::plugins::misc; use POSIX; @@ -167,9 +168,10 @@ sub new { bless $self, $class; $options{options}->add_options(arguments => { - 'filter-name:s' => { name => 'filter_name' }, - 'unit:s' => { name => 'unit', default => 'd' }, - 'full-as-incremental:s' => { name => 'full_as_incremental' } + 'filter-name:s' => { name => 'filter_name' }, + 'filter-database-state:s' => { name => 'filter_database_state' }, + 'unit:s' => { name => 'unit', default => 'd' }, + 'full-as-incremental:s' => { name => 'full_as_incremental' } }); 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 { my ($self, %options) = @_; @@ -191,7 +221,8 @@ sub manage_selection { my $query = q{ SELECT - a.name, + a.name, + a.status, a.recovery_model, DATEDIFF(SS, MAX(b.backup_finish_date), GETDATE()), DATEDIFF(SS, MAX(b.backup_start_date), MAX(b.backup_finish_date)), @@ -205,6 +236,7 @@ sub manage_selection { $query = q{ SELECT D.name AS [database_name], + D.state, D.recovery_model, BS1.last_backup, BS1.last_duration, @@ -231,8 +263,13 @@ sub manage_selection { my $map_type = { D => 'full', I => 'incremental', L => 'log' }; $self->{databases} = {}; 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 '' && $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] })) { $self->{databases}->{ $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 (defined($row->[2]) && ( + if ($row->[5] =~ /D|I/) { + if (defined($row->[3]) && ( $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->[2]; + $self->{databases}->{ $row->[0] }->{all}->{exec_seconds} > $row->[3])) { + $self->{databases}->{ $row->[0] }->{all}->{exec_seconds} = $row->[3]; $self->{databases}->{ $row->[0] }->{all}->{exec_human} = centreon::plugins::misc::change_seconds( - value => $row->[2] + value => $row->[3] ); - if (defined($row->[3])) { - $self->{databases}->{ $row->[0] }->{all}->{duration_seconds} = $row->[3]; + if (defined($row->[4])) { + $self->{databases}->{ $row->[0] }->{all}->{duration_seconds} = $row->[4]; $self->{databases}->{ $row->[0] }->{all}->{duration_human} = centreon::plugins::misc::change_seconds( - value => $row->[3] + value => $row->[4] ); } } } - if (defined($row->[2])) { - $self->{databases}->{ $row->[0] }->{ $map_type->{ $row->[4] } }->{exec_seconds} = $row->[2]; - $self->{databases}->{ $row->[0] }->{ $map_type->{ $row->[4] } }->{exec_human} = centreon::plugins::misc::change_seconds( - value => $row->[2] + if (defined($row->[3])) { + $self->{databases}->{ $row->[0] }->{ $map_type->{ $row->[5] } }->{exec_seconds} = $row->[3]; + $self->{databases}->{ $row->[0] }->{ $map_type->{ $row->[5] } }->{exec_human} = centreon::plugins::misc::change_seconds( + value => $row->[3] ); } - 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] + if (defined($row->[4])) { + $self->{databases}->{ $row->[0] }->{ $map_type->{ $row->[5] } }->{duration_seconds} = $row->[4]; + $self->{databases}->{ $row->[0] }->{ $map_type->{ $row->[5] } }->{duration_human} = centreon::plugins::misc::change_seconds( + value => $row->[4] ); } } @@ -303,6 +340,10 @@ Check MSSQL backup. Filter databases by name. +=item B<--filter-database-state> + +Filter databases by state. + =item B<--full-as-incremental> Last incremental backup time uses last full backup time only if full is newer than incremental. diff --git a/database/mssql/mode/databasessize.pm b/database/mssql/mode/databasessize.pm index 43e73630c..fe9bce044 100644 --- a/database/mssql/mode/databasessize.pm +++ b/database/mssql/mode/databasessize.pm @@ -24,6 +24,7 @@ use base qw(centreon::plugins::templates::counter); use strict; use warnings; +use database::mssql::mode::resources::types qw($database_state); sub custom_space_usage_perfdata { my ($self, %options) = @_; @@ -233,6 +234,7 @@ sub new { $options{options}->add_options(arguments => { 'filter-database:s' => { name => 'filter_database' }, + 'filter-database-state:s' => { name => 'filter_database_state' }, 'datafiles-maxsize:s' => { name => 'datafiles_maxsize' }, 'logfiles-maxsize:s' => { name => 'logfiles_maxsize' }, 'datafiles-maxsize-unlimited:s' => { name => 'datafiles_maxsize_unlimited' }, @@ -259,51 +261,59 @@ sub manage_selection { } } - $options{sql}->query(query => qq{ - EXEC sp_MSforeachdb 'USE [?] + $options{sql}->query(query => q{ SELECT - DB_NAME(), - [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' + D.name AS [database_name], + D.state + FROM sys.databases D }); + my $databases = $options{sql}->fetchall_arrayref(); + # limit can be: 'unlimited', 'overload', 'other'. $self->{databases} = {}; - while ($result = $options{sql}->fetchall_arrayref()) { - last if (scalar(@$result) <= 0); + foreach my $database (@$databases) { + 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) { - next if (!defined($row->[7])); + $options{sql}->query(query => qq{ + 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 + }); + + my $rows = $options{sql}->fetchall_arrayref(); + + foreach my $row (@$rows) { + next if (!defined($row->[6])); - if (defined($self->{option_results}->{filter_database}) && $self->{option_results}->{filter_database} ne '' && - $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] })) { - $self->{databases}->{ $row->[0] } = { - name => $row->[0], + if (!defined($self->{databases}->{$dbname})) { + $self->{databases}->{$dbname} = { + name => $dbname, datafiles => { - name => $row->[0], + name => $dbname, used_space => 0, total_space => 0, limit => 'other' }, logfiles => { - name => $row->[0], + name => $dbname, used_space => 0, total_space => 0, 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) - if ($row->[7] == -1) { - $self->{databases}->{ $row->[0] }->{$row->[3] . 'files'}->{limit} = 'unlimited'; + if ($row->[6] == -1) { + $self->{databases}->{$dbname}->{$row->[2] . 'files'}->{limit} = 'unlimited'; } if (defined($self->{option_results}->{check_underlying_disk})) { # look for the drives foreach my $drive_name (keys %$drives) { - if ($row->[2] =~ /^$drive_name/) { - if (($row->[7] > 0) && (($row->[7] * 8 * 1024) <= ($size + $drives->{$drive_name}))) { - $size = $row->[7] * 8 * 1024; - } elsif (!defined($unlimited_disk->{ $row->[0] . '_' . $row->[3] . 'files_' . $drive_name })) { + if ($row->[1] =~ /^$drive_name/) { + if (($row->[6] > 0) && (($row->[6] * 8 * 1024) <= ($size + $drives->{$drive_name}))) { + $size = $row->[6] * 8 * 1024; + } elsif (!defined($unlimited_disk->{ $dbname . '_' . $row->[2] . 'files_' . $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; } } - } elsif ($row->[7] > 0) { - $size = $row->[7] * 8 * 1024; + } elsif ($row->[6] > 0) { + $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). +=item B<--filter-database-state> + +Filter databases by state. + =item B<--datafiles-maxsize> Overload all data files max size (in MB). diff --git a/database/mssql/mode/listdatabases.pm b/database/mssql/mode/listdatabases.pm index ae4d8e71e..ae70ba0d2 100644 --- a/database/mssql/mode/listdatabases.pm +++ b/database/mssql/mode/listdatabases.pm @@ -24,6 +24,7 @@ use base qw(centreon::plugins::mode); use strict; use warnings; +use database::mssql::mode::resources::types qw($database_state); sub new { my ($class, %options) = @_; @@ -45,41 +46,47 @@ sub check_options { sub manage_selection { my ($self, %options) = @_; - $self->{sql} = $options{sql}; - $self->{sql}->connect(); - $self->{sql}->query(query => q{DBCC SQLPERF(LOGSPACE)}); + $options{sql}->connect(); + $options{sql}->query(query => q{ + SELECT + D.name AS [database_name], + D.state + FROM sys.databases D + }); 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) { - if (defined($self->{option_results}->{filter_database}) && $self->{option_results}->{filter_database} ne '' && - $$database[0] !~ /$self->{option_results}->{filter_database}/i) { - 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]), - }; - } + $databases->{ $_->[0] } = { + name => $_->[0], + state => $database_state->{ $_->[1] } + }; } + + return $databases; } sub run { my ($self, %options) = @_; - $self->manage_selection(%options); - foreach my $database (sort keys %{$self->{databases}}) { - $self->{output}->output_add(long_msg => sprintf("[name = %s] [total = %s]", - $self->{databases}->{$database}->{display}, $self->{databases}->{$database}->{total})); + my $databases = $self->manage_selection(%options); + foreach (values %$databases) { + $self->{output}->output_add( + long_msg => sprintf( + '[name: %s] [state: %s]', + $_->{name}, + $_->{state} + ) + ); } - $self->{output}->output_add(severity => 'OK', - short_msg => 'List databases:'); + $self->{output}->output_add( + severity => 'OK', + short_msg => 'List databases:' + ); $self->{output}->display(nolabel => 1, force_ignore_perfdata => 1, force_long_output => 1); $self->{output}->exit(); } @@ -87,36 +94,18 @@ sub run { sub disco_format { my ($self, %options) = @_; - $self->{output}->add_disco_format(elements => ['name', 'total']); + $self->{output}->add_disco_format(elements => ['name', 'state']); } sub disco_show { my ($self, %options) = @_; - $self->manage_selection(%options); - foreach my $database (sort keys %{$self->{databases}}) { - $self->{output}->add_disco_entry( - name => $self->{databases}->{$database}->{display}, - total => $self->{databases}->{$database}->{total}, - ); + my $databases = $self->manage_selection(%options); + foreach (values %$databases) { + $self->{output}->add_disco_entry(%$_); } } -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; __END__ diff --git a/database/mssql/mode/resources/types.pm b/database/mssql/mode/resources/types.pm new file mode 100644 index 000000000..0cae3d982 --- /dev/null +++ b/database/mssql/mode/resources/types.pm @@ -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; diff --git a/database/mssql/mode/tables.pm b/database/mssql/mode/tables.pm index c7220be2d..5ac18c70c 100644 --- a/database/mssql/mode/tables.pm +++ b/database/mssql/mode/tables.pm @@ -24,6 +24,7 @@ use base qw(centreon::plugins::templates::counter); use strict; use warnings; +use database::mssql::mode::resources::types qw($database_state); sub prefix_database_output { my ($self, %options) = @_; @@ -114,8 +115,9 @@ sub new { bless $self, $class; $options{options}->add_options(arguments => { - 'filter-database:s' => { name => 'filter_database' }, - 'filter-table:s' => { name => 'filter_table' } + 'filter-database:s' => { name => 'filter_database' }, + 'filter-database-state:s' => { name => 'filter_database_state' }, + 'filter-table:s' => { name => 'filter_table' } }); return $self; @@ -165,19 +167,20 @@ sub manage_selection { my ($self, %options) = @_; $options{sql}->connect(); - $options{sql}->query(query => qq{ - SELECT [name] as database_name - FROM sys.databases + $options{sql}->query(query => q{ + SELECT + D.name AS [database_name], + D.state + FROM sys.databases D }); $self->{databases} = {}; my $results = $options{sql}->fetchall_arrayref(); foreach my $row (@$results) { - if (defined($self->{option_results}->{filter_database}) && $self->{option_results}->{filter_database} ne '' && - $row->[0] !~ /$self->{option_results}->{filter_database}/) { - $self->{output}->output_add(long_msg => "skipping '" . $row->[0] . "': no matching filter.", debug => 1); - next - } + next if (defined($self->{option_results}->{filter_database}) && $self->{option_results}->{filter_database} ne '' && + $row->[0] !~ /$self->{option_results}->{filter_database}/); + next if (defined($self->{option_results}->{filter_database_state}) && $self->{option_results}->{filter_database_state} ne '' && + $database_state->{ $row->[1] } !~ /$self->{option_results}->{filter_database_state}/); $self->{databases}->{ $row->[0] } = { display => $row->[0], @@ -208,6 +211,10 @@ Check tables size. Filter tables by database name (Can be a regexp). +=item B<--filter-database-state> + +Filter databases by state. + =item B<--filter-table> Filter tables by name (can be a regexp).