Enhance exchange (#2269)
This commit is contained in:
parent
becca7e330
commit
3af2d6554a
|
@ -20,16 +20,239 @@
|
|||
|
||||
package apps::exchange::2010::local::mode::databases;
|
||||
|
||||
use base qw(centreon::plugins::mode);
|
||||
use base qw(centreon::plugins::templates::counter);
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use centreon::plugins::misc;
|
||||
use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng);
|
||||
use centreon::common::powershell::exchange::2010::databases;
|
||||
use apps::exchange::2010::local::mode::resources::types qw($copystatus_contentindexstate);
|
||||
use JSON::XS;
|
||||
|
||||
sub custom_mailflow_latency_perfdata {
|
||||
my ($self, %options) = @_;
|
||||
|
||||
$self->{output}->perfdata_add(
|
||||
nlabel => $self->{nlabel},
|
||||
unit => 's',
|
||||
instances => [$self->{result_values}->{server}, $self->{result_values}->{database}],
|
||||
value => sprintf('%.3f', $self->{result_values}->{mailflow_latency}),
|
||||
warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}),
|
||||
critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel})
|
||||
);
|
||||
}
|
||||
|
||||
sub custom_space_size_perfdata {
|
||||
my ($self, %options) = @_;
|
||||
|
||||
$self->{output}->perfdata_add(
|
||||
nlabel => $self->{nlabel},
|
||||
unit => 'B',
|
||||
instances => [$self->{result_values}->{server}, $self->{result_values}->{database}],
|
||||
value => $self->{result_values}->{size},
|
||||
warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}),
|
||||
critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel})
|
||||
);
|
||||
}
|
||||
|
||||
sub custom_space_available_perfdata {
|
||||
my ($self, %options) = @_;
|
||||
|
||||
$self->{output}->perfdata_add(
|
||||
nlabel => $self->{nlabel},
|
||||
unit => 'B',
|
||||
instances => [$self->{result_values}->{server}, $self->{result_values}->{database}],
|
||||
value => $self->{result_values}->{asize},
|
||||
warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}),
|
||||
critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel})
|
||||
);
|
||||
}
|
||||
|
||||
sub custom_mapi_output {
|
||||
my ($self, %options) = @_;
|
||||
|
||||
return 'mapi test connectivity is ' . $self->{result_values}->{mapi_result};
|
||||
}
|
||||
|
||||
sub custom_mailflow_output {
|
||||
my ($self, %options) = @_;
|
||||
|
||||
return 'mapi test result is ' . $self->{result_values}->{mailflow_result};
|
||||
}
|
||||
|
||||
sub custom_copystatus_output {
|
||||
my ($self, %options) = @_;
|
||||
|
||||
return sprintf(
|
||||
"copystatus state is %s [error: %s]",
|
||||
$self->{result_values}->{copystatus_indexstate},
|
||||
$self->{result_values}->{copystatus_content_index_error_message}
|
||||
);
|
||||
}
|
||||
|
||||
sub custom_status_output {
|
||||
my ($self, %options) = @_;
|
||||
|
||||
return sprintf(
|
||||
'%s mounted',
|
||||
$self->{result_values}->{mounted} == 0 ? 'not' : 'is'
|
||||
);
|
||||
}
|
||||
|
||||
sub database_long_output {
|
||||
my ($self, %options) = @_;
|
||||
|
||||
return "checking database '" . $options{instance_value}->{database} . "' server '" . $options{instance_value}->{server} . "'";
|
||||
}
|
||||
|
||||
sub prefix_database_output {
|
||||
my ($self, %options) = @_;
|
||||
|
||||
return "Database '" . $options{instance_value}->{database} . "' server '" . $options{instance_value}->{server} . "'";
|
||||
}
|
||||
|
||||
sub prefix_mailflow_output {
|
||||
my ($self, %options) = @_;
|
||||
|
||||
return 'mailflow ';
|
||||
}
|
||||
|
||||
sub prefix_global_output {
|
||||
my ($self, %options) = @_;
|
||||
|
||||
return 'Databases ';
|
||||
}
|
||||
|
||||
sub set_counters {
|
||||
my ($self, %options) = @_;
|
||||
|
||||
$self->{maps_counters_type} = [
|
||||
{ name => 'global', type => 0, cb_prefix_output => 'prefix_global_output', skipped_code => { -10 => 1 } },
|
||||
{ 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 => 'db_global', type => 0, skipped_code => { -10 => 1 } },
|
||||
{ name => 'space', type => 0, skipped_code => { -10 => 1 } },
|
||||
{ name => 'mapi', type => 0, skipped_code => { -10 => 1 } },
|
||||
{ name => 'mailflow', type => 0, cb_prefix_output => 'prefix_mailflow_output', skipped_code => { -10 => 1 } },
|
||||
{ name => 'copystatus', type => 0, skipped_code => { -10 => 1 } },
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
$self->{maps_counters}->{global} = [
|
||||
{ label => 'databases-space-size', nlabel => 'databases.space.size.bytes', set => {
|
||||
key_values => [ { name => 'size' } ],
|
||||
output_template => 'space size: %s %s',
|
||||
output_change_bytes => 1,
|
||||
perfdatas => [
|
||||
{ template => '%d', unit => 'B', min => 0 }
|
||||
]
|
||||
}
|
||||
},
|
||||
{ label => 'databases-space-available', nlabel => 'databases.space.available.bytes', set => {
|
||||
key_values => [ { name => 'asize' } ],
|
||||
output_template => 'space available: %s %s',
|
||||
output_change_bytes => 1,
|
||||
perfdatas => [
|
||||
{ template => '%d', unit => 'B', min => 0 }
|
||||
]
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
$self->{maps_counters}->{db_global} = [
|
||||
{
|
||||
label => 'status',
|
||||
type => 2,
|
||||
critical_default => '%{mounted} == 0',
|
||||
set => {
|
||||
key_values => [
|
||||
{ name => 'mounted' }, { name => 'database' }, { name => 'server' }
|
||||
],
|
||||
closure_custom_output => $self->can('custom_status_output'),
|
||||
closure_custom_perfdata => sub { return 0; },
|
||||
closure_custom_threshold_check => \&catalog_status_threshold_ng
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
$self->{maps_counters}->{space} = [
|
||||
{ label => 'database-space-size', nlabel => 'database.space.size.bytes', set => {
|
||||
key_values => [ { name => 'size' }, { name => 'database' }, { name => 'server' } ],
|
||||
output_template => 'space size: %s %s',
|
||||
output_change_bytes => 1,
|
||||
closure_custom_perfdata => $self->can('custom_space_size_perfdata')
|
||||
}
|
||||
},
|
||||
{ label => 'database-space-available', nlabel => 'database.space.available.bytes', set => {
|
||||
key_values => [ { name => 'asize' }, { name => 'database' }, { name => 'server' } ],
|
||||
output_template => 'space available: %s %s',
|
||||
output_change_bytes => 1,
|
||||
closure_custom_perfdata => $self->can('custom_space_asize_perfdata')
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
$self->{maps_counters}->{mapi} = [
|
||||
{
|
||||
label => 'mapi',
|
||||
type => 2,
|
||||
critical_default => '%{mapi_result} !~ /Success/i',
|
||||
set => {
|
||||
key_values => [
|
||||
{ name => 'mapi_result' }, { name => 'database' }, { name => 'server' }
|
||||
],
|
||||
closure_custom_output => $self->can('custom_mapi_output'),
|
||||
closure_custom_perfdata => sub { return 0; },
|
||||
closure_custom_threshold_check => \&catalog_status_threshold_ng
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
$self->{maps_counters}->{mailflow} = [
|
||||
{
|
||||
label => 'mailflow',
|
||||
type => 2,
|
||||
critical_default => '%{mailflow_result} !~ /Success/i',
|
||||
set => {
|
||||
key_values => [
|
||||
{ name => 'mailflow_result' }, { name => 'database' }, { name => 'server' }
|
||||
],
|
||||
closure_custom_output => $self->can('custom_mailflow_output'),
|
||||
closure_custom_perfdata => sub { return 0; },
|
||||
closure_custom_threshold_check => \&catalog_status_threshold_ng
|
||||
}
|
||||
},
|
||||
{ label => 'mailflow-latency', nlabel => 'database.mailflow.latency.seconds', display_ok => 0, set => {
|
||||
key_values => [ { name => 'mailflow_latency' }, { name => 'database' }, { name => 'server' } ],
|
||||
output_template => 'latency: %.3f %%',
|
||||
closure_custom_perfdata => $self->can('custom_mailflow_latency_perfdata')
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
$self->{maps_counters}->{copystatus} = [
|
||||
{
|
||||
label => 'copystatus',
|
||||
type => 2,
|
||||
critical_default => '%{copystatus_indexstate} !~ /Healthy/i',
|
||||
set => {
|
||||
key_values => [
|
||||
{ name => 'copystatus_indexstate' }, { name => 'copystatus_content_index_error_message' },
|
||||
{ name => 'database' }, { name => 'server' }
|
||||
],
|
||||
closure_custom_output => $self->can('custom_copystatus_output'),
|
||||
closure_custom_perfdata => sub { return 0; },
|
||||
closure_custom_threshold_check => \&catalog_status_threshold_ng
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
sub new {
|
||||
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;
|
||||
|
||||
$options{options}->add_options(arguments => {
|
||||
|
@ -47,36 +270,13 @@ sub new {
|
|||
'ps-exec-only' => { name => 'ps_exec_only' },
|
||||
'ps-display' => { name => 'ps_display' },
|
||||
'ps-database-filter:s' => { name => 'ps_database_filter' },
|
||||
'ps-database-test-filter:s' => { name => 'ps_database_test_filter' },
|
||||
'warning-mapi:s' => { name => 'warning_mapi' },
|
||||
'critical-mapi:s' => { name => 'critical_mapi', default => '%{mapi_result} !~ /Success/i' },
|
||||
'warning-mailflow:s' => { name => 'warning_mailflow' },
|
||||
'critical-mailflow:s' => { name => 'critical_mailflow', default => '%{mailflow_result} !~ /Success/i' },
|
||||
'warning-copystatus:s' => { name => 'warning_copystatus' },
|
||||
'critical-copystatus:s' => { name => 'critical_copystatus', default => '%{copystatus_indexstate} !~ /Healthy/i' },
|
||||
'ps-database-test-filter:s' => { name => 'ps_database_test_filter' }
|
||||
});
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub change_macros {
|
||||
my ($self, %options) = @_;
|
||||
|
||||
foreach (('warning_mapi', 'critical_mapi', 'warning_mailflow', 'critical_mailflow', 'warning_copystatus', 'critical_copystatus')) {
|
||||
if (defined($self->{option_results}->{$_})) {
|
||||
$self->{option_results}->{$_} =~ s/%\{(.*?)\}/\$self->{data}->{$1}/g;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub check_options {
|
||||
my ($self, %options) = @_;
|
||||
$self->SUPER::init(%options);
|
||||
|
||||
$self->change_macros();
|
||||
}
|
||||
|
||||
sub run {
|
||||
sub manage_selection {
|
||||
my ($self, %options) = @_;
|
||||
|
||||
if (!defined($self->{option_results}->{no_ps})) {
|
||||
|
@ -117,10 +317,35 @@ sub run {
|
|||
$self->{output}->display(nolabel => 1, force_ignore_perfdata => 1, force_long_output => 1);
|
||||
$self->{output}->exit();
|
||||
}
|
||||
centreon::common::powershell::exchange::2010::databases::check($self, stdout => $stdout);
|
||||
|
||||
$self->{output}->display();
|
||||
$self->{output}->exit();
|
||||
my $decoded;
|
||||
eval {
|
||||
$decoded = JSON::XS->new->decode($stdout);
|
||||
};
|
||||
if ($@) {
|
||||
$self->{output}->add_option_msg(short_msg => "Cannot decode json response: $@");
|
||||
$self->{output}->option_exit();
|
||||
}
|
||||
|
||||
$self->{global} = { size => 0, asize => 0 };
|
||||
$self->{databases} = {};
|
||||
foreach my $db (@$decoded) {
|
||||
$db->{mounted} = $db->{mounted} =~ /True|1/i ? 1 : 0;
|
||||
$db->{copystatus_indexstate} = $copystatus_contentindexstate->{ $db->{copystatus_indexstate} }
|
||||
if (defined($db->{copystatus_indexstate}));
|
||||
|
||||
$self->{databases}->{ $db->{database} . ':' . $db->{server} } = {
|
||||
database => $db->{database},
|
||||
server => $db->{server},
|
||||
db_global => $db,
|
||||
space => $db,
|
||||
mapi => $db,
|
||||
mailflow => $db,
|
||||
copystatus => $db
|
||||
};
|
||||
$self->{global}->{size} += $db->{size};
|
||||
$self->{global}->{asize} += $db->{asize};
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
|
@ -194,6 +419,16 @@ Filter database (only wilcard '*' can be used. In Powershell).
|
|||
|
||||
Skip mapi/mailflow test (regexp can be used. In Powershell).
|
||||
|
||||
=item B<--warning-status>
|
||||
|
||||
Set warning threshold.
|
||||
Can used special variables like: %{mounted}, %{database}, %{server}
|
||||
|
||||
=item B<--critical-status>
|
||||
|
||||
Set critical threshold (Default: '%{mounted} == 0').
|
||||
Can used special variables like: %{mounted}, %{database}, %{server}
|
||||
|
||||
=item B<--warning-mapi>
|
||||
|
||||
Set warning threshold.
|
||||
|
|
|
@ -20,12 +20,58 @@
|
|||
|
||||
package apps::exchange::2010::local::mode::queues;
|
||||
|
||||
use base qw(centreon::plugins::mode);
|
||||
use base qw(centreon::plugins::templates::counter);
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use centreon::plugins::misc;
|
||||
use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng);
|
||||
use centreon::common::powershell::exchange::2010::queues;
|
||||
use apps::exchange::2010::local::mode::resources::types qw($queue_status $queue_delivery_type);
|
||||
use JSON::XS;
|
||||
|
||||
sub custom_status_output {
|
||||
my ($self, %options) = @_;
|
||||
|
||||
return sprintf(
|
||||
'status: %s [last error: %s] [delivery type: %s] [identity: %s] [message count: %s]',
|
||||
$self->{result_values}->{status},
|
||||
$self->{result_values}->{last_error},
|
||||
$self->{result_values}->{delivery_type},
|
||||
$self->{result_values}->{identity},
|
||||
$self->{result_values}->{message_count}
|
||||
);
|
||||
}
|
||||
|
||||
sub prefix_queue_output {
|
||||
my ($self, %options) = @_;
|
||||
|
||||
return "Queue '" . $options{instance_value}->{nexthopdomain} . "' ";
|
||||
}
|
||||
|
||||
sub set_counters {
|
||||
my ($self, %options) = @_;
|
||||
|
||||
$self->{maps_counters_type} = [
|
||||
{ name => 'queues', type => 1, cb_prefix_output => 'prefix_queue_output', message_multiple => 'All queues are ok', skipped_code => { -11 => 1 } }
|
||||
];
|
||||
|
||||
$self->{maps_counters}->{queues} = [
|
||||
{ label => 'status', type => 2, critical_default => '%{status} !~ /Ready|Active/i', set => {
|
||||
key_values => [
|
||||
{ name => 'nexthopdomain' }, { name => 'identity' },
|
||||
{ name => 'is_valid' }, { name => 'isvalid' },
|
||||
{ name => 'delivery_type' }, { name => 'deliverytype' },
|
||||
{ name => 'message_count' }, { name => 'messagecount' },
|
||||
{ name => 'status' }, { name => 'last_error' }
|
||||
],
|
||||
closure_custom_output => $self->can('custom_status_output'),
|
||||
closure_custom_perfdata => sub { return 0; },
|
||||
closure_custom_threshold_check => \&catalog_status_threshold_ng
|
||||
}
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
sub new {
|
||||
my ($class, %options) = @_;
|
||||
|
@ -42,32 +88,13 @@ sub new {
|
|||
'command-path:s' => { name => 'command_path' },
|
||||
'command-options:s' => { name => 'command_options', default => '-InputFormat none -NoLogo -EncodedCommand' },
|
||||
'ps-exec-only' => { name => 'ps_exec_only' },
|
||||
'ps-display' => { name => 'ps_display' },
|
||||
'warning:s' => { name => 'warning' },
|
||||
'critical:s' => { name => 'critical', default => '%{status} !~ /Ready|Active/i' },
|
||||
'ps-display' => { name => 'ps_display' }
|
||||
});
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub change_macros {
|
||||
my ($self, %options) = @_;
|
||||
|
||||
foreach (('warning', 'critical')) {
|
||||
if (defined($self->{option_results}->{$_})) {
|
||||
$self->{option_results}->{$_} =~ s/%\{(.*?)\}/\$self->{data}->{$1}/g;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub check_options {
|
||||
my ($self, %options) = @_;
|
||||
$self->SUPER::init(%options);
|
||||
|
||||
$self->change_macros();
|
||||
}
|
||||
|
||||
sub run {
|
||||
sub manage_selection {
|
||||
my ($self, %options) = @_;
|
||||
|
||||
if (!defined($self->{option_results}->{no_ps})) {
|
||||
|
@ -103,10 +130,52 @@ sub run {
|
|||
$self->{output}->display(nolabel => 1, force_ignore_perfdata => 1, force_long_output => 1);
|
||||
$self->{output}->exit();
|
||||
}
|
||||
centreon::common::powershell::exchange::2010::queues::check($self, stdout => $stdout);
|
||||
|
||||
$self->{output}->display();
|
||||
$self->{output}->exit();
|
||||
my $decoded;
|
||||
eval {
|
||||
$decoded = JSON::XS->new->decode($stdout);
|
||||
};
|
||||
if ($@) {
|
||||
$self->{output}->add_option_msg(short_msg => "Cannot decode json response: $@");
|
||||
$self->{output}->option_exit();
|
||||
}
|
||||
|
||||
my $perfdatas_queues = {};
|
||||
|
||||
$self->{queues} = {};
|
||||
foreach my $queue (@$decoded) {
|
||||
$queue->{is_valid} = $queue->{is_valid} =~ /True|1/i ? 1 : 0;
|
||||
$queue->{status} = $queue_status->{ $queue->{status} }
|
||||
if (defined($queue->{status}));
|
||||
$queue->{delivery_type} = $queue_delivery_type->{ $queue->{delivery_type} }
|
||||
if (defined($queue->{delivery_type}));
|
||||
|
||||
$self->{queues}->{ $queue->{identity} } = {
|
||||
%$queue,
|
||||
deliverytype => $queue->{delivery_type},
|
||||
isvalid => $queue->{is_valid},
|
||||
messagecount => $queue->{message_count}
|
||||
};
|
||||
|
||||
if ($queue->{message_count} =~ /^(\d+)/) {
|
||||
my $num = $1;
|
||||
my $identity = $queue->{identity};
|
||||
|
||||
$identity = $1 if ($queue->{identity} =~ /^(.*\/)[0-9]+$/);
|
||||
$perfdatas_queues->{$identity} = 0 if (!defined($perfdatas_queues->{$identity}));
|
||||
$perfdatas_queues->{$identity} += $num;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (keys %$perfdatas_queues) {
|
||||
$self->{output}->perfdata_add(
|
||||
label => 'queue_length',
|
||||
nlabel => 'queue.length.count',
|
||||
instances => $_,
|
||||
value => $perfdatas_queues->{$_},
|
||||
min => 0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
|
@ -160,15 +229,15 @@ Display powershell script.
|
|||
|
||||
Print powershell output.
|
||||
|
||||
=item B<--warning>
|
||||
=item B<--warning-status>
|
||||
|
||||
Set warning threshold.
|
||||
Can used special variables like: %{status}, %{identity}, %{isvalid}, %{deliverytype}, %{messagecount}
|
||||
Can used special variables like: %{status}, %{identity}, %{is_valid}, %{delivery_type}, %{message_count}
|
||||
|
||||
=item B<--critical>
|
||||
=item B<--critical-status>
|
||||
|
||||
Set critical threshold (Default: '%{status} !~ /Ready|Active/i').
|
||||
Can used special variables like: %{status}, %{identity}, %{isvalid}, %{deliverytype}, %{messagecount}
|
||||
Can used special variables like: %{status}, %{identity}, %{is_valid}, %{delivery_type}, %{message_count}
|
||||
|
||||
=back
|
||||
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
#
|
||||
# Copyright 2020 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 apps::exchange::2010::local::mode::resources::types;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Exporter;
|
||||
|
||||
our $queue_status;
|
||||
our $queue_delivery_type;
|
||||
our $copystatus_contentindexstate;
|
||||
|
||||
our @ISA = qw(Exporter);
|
||||
our @EXPORT_OK = qw(
|
||||
$queue_status $queue_delivery_type
|
||||
$copystatus_contentindexstate
|
||||
);
|
||||
|
||||
$queue_status = {
|
||||
0 => 'None',
|
||||
1 => 'Active',
|
||||
2 => 'Ready',
|
||||
3 => 'Retry',
|
||||
4 => 'Suspended',
|
||||
5 => 'Connecting',
|
||||
6 => 'Throttled'
|
||||
};
|
||||
|
||||
$queue_delivery_type = {
|
||||
0 => 'Undefined',
|
||||
1 => 'DnsConnectorDelivery',
|
||||
2 => 'MapiDelivery',
|
||||
3 => 'NonSmtpGatewayDelivery',
|
||||
4 => 'SmartHostConnectorDelivery',
|
||||
5 => 'SmtpRelayToRemoteAdSite',
|
||||
6 => 'SmtpRelayToTiRg',
|
||||
7 => 'SmtpRelayWithinAdSite',
|
||||
8 => 'SmtpRelayWithinAdSiteToEdge',
|
||||
9 => 'Unreachable',
|
||||
10 => 'ShadowRedundancy',
|
||||
11 => 'Heartbeat',
|
||||
12 => 'DeliveryAgent',
|
||||
13 => 'SmtpDeliveryToMailbox',
|
||||
14 => 'SmtpRelayToDag',
|
||||
15 => 'SmtpRelayToMailboxDeliveryGroup',
|
||||
16 => 'SmtpRelayToConnectorSourceServers',
|
||||
17 => 'SmtpRelayToServers',
|
||||
18 => 'SmtpRelayToRemoteForest',
|
||||
19 => 'SmtpDeliveryToExo',
|
||||
20 => 'HttpDeliveryToMailbox',
|
||||
21 => 'HttpDeliveryToExo',
|
||||
22 => 'Delay',
|
||||
23 => 'SmtpSubmissionToEop',
|
||||
24 => 'SmtpSubmissionToExo',
|
||||
25 => 'HttpDeliveryToApp'
|
||||
};
|
||||
|
||||
$copystatus_contentindexstate = {
|
||||
0 => 'Unknown',
|
||||
1 => 'Healthy',
|
||||
2 => 'Crawling',
|
||||
3 => 'Failed',
|
||||
4 => 'Seeding',
|
||||
5 => 'FailedAndSuspended',
|
||||
6 => 'Suspended',
|
||||
7 => 'Disabled',
|
||||
8 => 'AutoSuspended',
|
||||
9 => 'HealthyAndUpgrading',
|
||||
10 => 'DiskUnavailable'
|
||||
};
|
||||
|
||||
1;
|
|
@ -30,7 +30,7 @@ sub new {
|
|||
bless $self, $class;
|
||||
|
||||
$self->{version} = '0.1';
|
||||
%{$self->{modes}} = (
|
||||
$self->{modes} = {
|
||||
'activesync-mailbox' => 'apps::exchange::2010::local::mode::activesyncmailbox',
|
||||
'databases' => 'apps::exchange::2010::local::mode::databases',
|
||||
'list-databases' => 'apps::exchange::2010::local::mode::listdatabases',
|
||||
|
@ -40,8 +40,8 @@ sub new {
|
|||
'owa-mailbox' => 'apps::exchange::2010::local::mode::owamailbox',
|
||||
'queues' => 'apps::exchange::2010::local::mode::queues',
|
||||
'replication-health' => 'apps::exchange::2010::local::mode::replicationhealth',
|
||||
'services' => 'apps::exchange::2010::local::mode::services',
|
||||
);
|
||||
'services' => 'apps::exchange::2010::local::mode::services'
|
||||
};
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
@ -52,7 +52,6 @@ __END__
|
|||
|
||||
=head1 PLUGIN DESCRIPTION
|
||||
|
||||
Check Windows Exchange 2010 locally.
|
||||
!!! Experimental system !!!
|
||||
Check Windows Exchange 2010/2016 locally.
|
||||
|
||||
=cut
|
||||
|
|
|
@ -22,8 +22,8 @@ package centreon::common::powershell::exchange::2010::databases;
|
|||
|
||||
use strict;
|
||||
use warnings;
|
||||
use centreon::plugins::misc;
|
||||
use centreon::common::powershell::exchange::2010::powershell;
|
||||
use centreon::common::powershell::functions;
|
||||
|
||||
sub get_powershell {
|
||||
my (%options) = @_;
|
||||
|
@ -33,7 +33,9 @@ sub get_powershell {
|
|||
my $no_copystatus = (defined($options{no_copystatus})) ? 1 : 0;
|
||||
|
||||
my $ps = centreon::common::powershell::exchange::2010::powershell::powershell_init(%options);
|
||||
|
||||
$ps .= centreon::common::powershell::functions::escape_jsonstring(%options);
|
||||
$ps .= centreon::common::powershell::functions::convert_to_json(%options);
|
||||
|
||||
$ps .= '
|
||||
# Check to make sure all databases are mounted
|
||||
try {
|
||||
|
@ -55,15 +57,21 @@ try {
|
|||
Write-Host $Error[0].Exception
|
||||
exit 1
|
||||
}
|
||||
|
||||
$items = New-Object System.Collections.Generic.List[Hashtable];
|
||||
Foreach ($DB in $MountedDB) {
|
||||
Write-Host "[name=" $DB.Name "][server=" $DB.Server "][mounted=" $DB.Mounted "][size=" $DB.DatabaseSize "][asize=" $DB.AvailableNewMailboxSpace "]" -NoNewline
|
||||
|
||||
$item = @{}
|
||||
|
||||
$item.database = $DB.Name
|
||||
$item.server = $DB.Server.Name
|
||||
$item.mounted = $DB.Mounted
|
||||
$item.size = $DB.DatabaseSize.ToBytes().ToString()
|
||||
$item.asize = $DB.AvailableNewMailboxSpace.ToBytes().ToString()
|
||||
';
|
||||
|
||||
if (defined($options{filter_database_test}) && $options{filter_database_test} ne '') {
|
||||
$ps .= '
|
||||
if (!($DB.Name -match "' . $options{filter_database_test} . '")) {
|
||||
Write-Host "[Skip extra test]"
|
||||
continue
|
||||
}
|
||||
';
|
||||
|
@ -77,7 +85,7 @@ Foreach ($DB in $MountedDB) {
|
|||
$ps .= '
|
||||
# Test Mapi Connectivity
|
||||
$MapiResult = test-mapiconnectivity -Database $DB.Name
|
||||
Write-Host "[mapi=" $MapiResult.Result "]" -NoNewline
|
||||
$item.mapi_result = $MapiResult.Result
|
||||
';
|
||||
}
|
||||
|
||||
|
@ -85,7 +93,8 @@ Foreach ($DB in $MountedDB) {
|
|||
$ps .= '
|
||||
# Test Mailflow
|
||||
$MailflowResult = Test-mailflow -Targetdatabase $DB.Name
|
||||
Write-Host "[mailflow=" $MailflowResult.testmailflowresult "][latency=" $MailflowResult.MessageLatencyTime.TotalMilliseconds "]" -NoNewline
|
||||
$item.mailflow_result = $MailflowResult.testmailflowresult
|
||||
$item.mailflow_latency = $MailflowResult.MessageLatencyTime.TotalMilliseconds
|
||||
';
|
||||
}
|
||||
if ($no_copystatus == 0) {
|
||||
|
@ -93,214 +102,25 @@ Foreach ($DB in $MountedDB) {
|
|||
# Test CopyStatus
|
||||
$tmp_name = $DB.Name + "\" + $DB.Server
|
||||
$CopyStatusResult = Get-MailboxDatabaseCopyStatus -Identity $tmp_name
|
||||
Write-Host "[contentindexstate=" $CopyStatusResult.ContentIndexState "][[contentindexerrormessage=" $CopyStatusResult.ContentIndexErrorMessage "]]" -NoNewline
|
||||
$item.copystatus_indexstate = $CopyStatusResult.ContentIndexState.value__
|
||||
$item.copystatus_content_index_error_message = $CopyStatusResult.ContentIndexErrorMessage
|
||||
';
|
||||
}
|
||||
|
||||
$ps .= '
|
||||
}
|
||||
Write-Host ""
|
||||
|
||||
$items.Add($item)
|
||||
}
|
||||
|
||||
$jsonString = $items | ConvertTo-JSON-20 -forceArray $true
|
||||
Write-Host $jsonString
|
||||
exit 0
|
||||
';
|
||||
|
||||
return $ps;
|
||||
}
|
||||
|
||||
sub check_mapi {
|
||||
my ($self, %options) = @_;
|
||||
|
||||
if (defined($self->{option_results}->{no_mapi})) {
|
||||
$self->{output}->output_add(long_msg => ' Skip MAPI test connectivity');
|
||||
return ;
|
||||
}
|
||||
|
||||
if ($options{line} !~ /\[mapi=(.*?)\]/) {
|
||||
$self->{output}->output_add(long_msg => ' Skip MAPI test connectivity (information not found)');
|
||||
return ;
|
||||
}
|
||||
|
||||
$self->{data}->{mapi_result} = centreon::plugins::misc::trim($1);
|
||||
$self->{output}->output_add(long_msg => " MAPI Test connectivity: " . $self->{data}->{mapi_result});
|
||||
|
||||
my ($status, $message) = ('ok');
|
||||
eval {
|
||||
local $SIG{__WARN__} = sub { $message = $_[0]; };
|
||||
local $SIG{__DIE__} = sub { $message = $_[0]; };
|
||||
|
||||
if (defined($self->{option_results}->{critical_mapi}) && $self->{option_results}->{critical_mapi} ne '' &&
|
||||
eval "$self->{option_results}->{critical_mapi}") {
|
||||
$status = 'critical';
|
||||
} elsif (defined($self->{option_results}->{warning_mapi}) && $self->{option_results}->{warning_mapi} ne '' &&
|
||||
eval "$self->{option_results}->{warning_mapi}") {
|
||||
$status = 'warning';
|
||||
}
|
||||
};
|
||||
if (defined($message)) {
|
||||
$self->{output}->output_add(long_msg => 'filter status issue: ' . $message);
|
||||
}
|
||||
if (!$self->{output}->is_status(value => $status, compare => 'ok', litteral => 1)) {
|
||||
$self->{output}->output_add(severity => $status,
|
||||
short_msg => sprintf("Server '%s' Database '%s' MAPI connectivity is %s",
|
||||
$self->{data}->{server}, $self->{data}->{database}, $self->{data}->{mapi_result}));
|
||||
}
|
||||
}
|
||||
|
||||
sub check_mailflow {
|
||||
my ($self, %options) = @_;
|
||||
|
||||
if (defined($self->{option_results}->{no_mailflow})) {
|
||||
$self->{output}->output_add(long_msg => ' Skip Mailflow test');
|
||||
return ;
|
||||
}
|
||||
|
||||
if ($options{line} !~ /\[mailflow=(.*?)\]\[latency=(.*?)\]/) {
|
||||
$self->{output}->output_add(long_msg => ' Skip Mailflow test (information not found)');
|
||||
return ;
|
||||
}
|
||||
|
||||
$self->{data}->{mailflow_result} = centreon::plugins::misc::trim($1);
|
||||
my $latency = centreon::plugins::misc::trim($2);
|
||||
$self->{output}->output_add(long_msg => " Mailflow Test: " . $self->{data}->{mailflow_result});
|
||||
|
||||
my ($status, $message) = ('ok');
|
||||
eval {
|
||||
local $SIG{__WARN__} = sub { $message = $_[0]; };
|
||||
local $SIG{__DIE__} = sub { $message = $_[0]; };
|
||||
|
||||
if (defined($self->{option_results}->{critical_mailflow}) && $self->{option_results}->{critical_mailflow} ne '' &&
|
||||
eval "$self->{option_results}->{critical_mailflow}") {
|
||||
$status = 'critical';
|
||||
} elsif (defined($self->{option_results}->{warning_mailflow}) && $self->{option_results}->{warning_mailflow} ne '' &&
|
||||
eval "$self->{option_results}->{warning_mailflow}") {
|
||||
$status = 'warning';
|
||||
}
|
||||
};
|
||||
if (defined($message)) {
|
||||
$self->{output}->output_add(long_msg => 'filter status issue: ' . $message);
|
||||
}
|
||||
if (!$self->{output}->is_status(value => $status, compare => 'ok', litteral => 1)) {
|
||||
$self->{output}->output_add(severity => $status,
|
||||
short_msg => sprintf("Server '%s' Database '%s' Mailflow test is %s",
|
||||
$self->{data}->{server}, $self->{data}->{database}, $self->{data}->{mailflow_result}));
|
||||
}
|
||||
|
||||
if ($latency =~ /^(\d+)/) {
|
||||
$self->{output}->perfdata_add(label => 'latency_' . $self->{data}->{server} . '_' . $self->{data}->{database}, unit => 's',
|
||||
value => sprintf("%.3f", $1 / 1000),
|
||||
min => 0);
|
||||
}
|
||||
}
|
||||
|
||||
sub check_copystatus {
|
||||
my ($self, %options) = @_;
|
||||
|
||||
if (defined($self->{option_results}->{no_copystatus})) {
|
||||
$self->{output}->output_add(long_msg => ' Skip copy status test');
|
||||
return ;
|
||||
}
|
||||
|
||||
if ($options{line} !~ /\[contentindexstate=(.*?)\]\[\[contentindexerrormessage=(.*?)\]\]/) {
|
||||
$self->{output}->output_add(long_msg => ' Skip copystatus test (information not found)');
|
||||
return ;
|
||||
}
|
||||
|
||||
($self->{data}->{copystatus_indexstate}, $self->{data}->{copystatus_indexerror}) = (centreon::plugins::misc::trim($1), centreon::plugins::misc::trim($2));
|
||||
$self->{output}->output_add(long_msg => " Copystatus state : " . $self->{data}->{copystatus_indexstate});
|
||||
|
||||
my ($status, $message) = ('ok');
|
||||
eval {
|
||||
local $SIG{__WARN__} = sub { $message = $_[0]; };
|
||||
local $SIG{__DIE__} = sub { $message = $_[0]; };
|
||||
|
||||
if (defined($self->{option_results}->{critical_copystatus}) && $self->{option_results}->{critical_copystatus} ne '' &&
|
||||
eval "$self->{option_results}->{critical_copystatus}") {
|
||||
$status = 'critical';
|
||||
} elsif (defined($self->{option_results}->{warning_copystatus}) && $self->{option_results}->{warning_copystatus} ne '' &&
|
||||
eval "$self->{option_results}->{warning_copystatus}") {
|
||||
$status = 'warning';
|
||||
}
|
||||
};
|
||||
if (defined($message)) {
|
||||
$self->{output}->output_add(long_msg => 'filter status issue: ' . $message);
|
||||
}
|
||||
if (!$self->{output}->is_status(value => $status, compare => 'ok', litteral => 1)) {
|
||||
$self->{output}->output_add(severity => $status,
|
||||
short_msg => sprintf("Server '%s' Database '%s' copystatus state is %s [error: %s]",
|
||||
$self->{data}->{server}, $self->{data}->{database}, $self->{data}->{copystatus_indexstate}, $self->{data}->{copystatus_indexerror}));
|
||||
}
|
||||
}
|
||||
|
||||
sub check {
|
||||
my ($self, %options) = @_;
|
||||
# options: stdout
|
||||
|
||||
# Following output:
|
||||
#[name= Mailbox Database 0975194476 ][server= SRVI-WIN-TEST ][mounted= True ][size= 136.1 MB (142,671,872 bytes) ][asize= 124.4 MB (130,482,176 bytes) ][mapi= Success ][mailflow= Success ][latency= 50,00 ]
|
||||
#...
|
||||
|
||||
$self->{output}->output_add(severity => 'OK',
|
||||
short_msg => 'Databases are mounted');
|
||||
if (!defined($self->{option_results}->{no_mapi})) {
|
||||
$self->{output}->output_add(severity => 'OK',
|
||||
short_msg => 'MAPI Connectivities are ok');
|
||||
}
|
||||
if (!defined($self->{option_results}->{no_mailflow})) {
|
||||
$self->{output}->output_add(severity => 'OK',
|
||||
short_msg => 'Mailflow test are ok');
|
||||
}
|
||||
|
||||
my $checked = 0;
|
||||
foreach my $line (split /\n/, $options{stdout}) {
|
||||
next if ($line !~ /^\[name=(.*?)\]\[server=(.*?)\]\[mounted=(.*?)\]\[size=(.*?)\]\[asize=(.*?)\]/);
|
||||
$checked++;
|
||||
$self->{data} = {};
|
||||
($self->{data}->{database}, $self->{data}->{server}, $self->{data}->{mounted}, $self->{data}->{size}, $self->{data}->{asize}) = (centreon::plugins::misc::trim($1), centreon::plugins::misc::trim($2),
|
||||
centreon::plugins::misc::trim($3), centreon::plugins::misc::trim($4), centreon::plugins::misc::trim($5));
|
||||
|
||||
$self->{output}->output_add(long_msg => sprintf("Test database '%s' server '%s':", $self->{data}->{database}, $self->{data}->{server}));
|
||||
if ($self->{data}->{asize} =~ /\((.*?)\s*bytes/) {
|
||||
my $free_bytes = $1;
|
||||
$free_bytes =~ s/[.,]//g;
|
||||
|
||||
my $total_bytes;
|
||||
if ($self->{data}->{size} =~ /\((.*?)\s*bytes/) {
|
||||
$total_bytes = $1;
|
||||
$total_bytes =~ s/[.,]//g;
|
||||
my ($total_value, $total_unit) = $self->{perfdata}->change_bytes(value => $total_bytes);
|
||||
$self->{output}->output_add(long_msg => sprintf(" Size %s", $total_value . ' ' . $total_unit));
|
||||
}
|
||||
my $used_bytes = $total_bytes - $free_bytes;
|
||||
|
||||
$self->{output}->perfdata_add(label => 'used_' . $self->{data}->{database}, unit => 'B',
|
||||
value => $used_bytes,
|
||||
min => 0, max => $total_bytes);
|
||||
my ($used_value, $used_unit) = $self->{perfdata}->change_bytes(value => $used_bytes);
|
||||
$self->{output}->output_add(long_msg => sprintf(" Used Size %s", $used_value . ' ' . $used_unit));
|
||||
}
|
||||
|
||||
|
||||
# Check mounted
|
||||
if ($self->{data}->{mounted} =~ /False/i) {
|
||||
$self->{output}->output_add(long_msg => sprintf(" not mounted\n Skip mapi/mailflow test"));
|
||||
$self->{output}->output_add(severity => 'CRITICAL',
|
||||
short_msg => sprintf("Database '%s' server '%s' is not mounted", $self->{data}->{database}, $self->{data}->{server}));
|
||||
next;
|
||||
}
|
||||
$self->{output}->output_add(long_msg => sprintf(" mounted"));
|
||||
|
||||
check_mapi($self, line => $line);
|
||||
check_mailflow($self, line => $line);
|
||||
check_copystatus($self, line => $line);
|
||||
}
|
||||
|
||||
if ($checked == 0) {
|
||||
$self->{output}->output_add(severity => 'UNKNOWN',
|
||||
short_msg => 'Cannot find informations');
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
|
|
@ -22,13 +22,15 @@ package centreon::common::powershell::exchange::2010::queues;
|
|||
|
||||
use strict;
|
||||
use warnings;
|
||||
use centreon::plugins::misc;
|
||||
use centreon::common::powershell::exchange::2010::powershell;
|
||||
use centreon::common::powershell::functions;
|
||||
|
||||
sub get_powershell {
|
||||
my (%options) = @_;
|
||||
|
||||
my $ps = centreon::common::powershell::exchange::2010::powershell::powershell_init(%options);
|
||||
$ps .= centreon::common::powershell::functions::escape_jsonstring(%options);
|
||||
$ps .= centreon::common::powershell::functions::convert_to_json(%options);
|
||||
|
||||
$ps .= '
|
||||
try {
|
||||
|
@ -39,80 +41,28 @@ try {
|
|||
exit 1
|
||||
}
|
||||
|
||||
$items = New-Object System.Collections.Generic.List[Hashtable];
|
||||
Foreach ($result in $results) {
|
||||
Write-Host "[identity=" $result.Identity "][nexthopdomain=" $result.NextHopDomain "][deliverytype=" $result.DeliveryType "][status=" $result.Status "][isvalid=" $result.IsValid "][messagecount=" $result.MessageCount "][[error=" $result.LastError "]]"
|
||||
$item = @{}
|
||||
|
||||
$item.identity = $result.Identity.ToString().Replace("\\", "/")
|
||||
$item.nexthopdomain = $result.NextHopDomain
|
||||
$item.delivery_type = $result.DeliveryType.value__
|
||||
$item.status = $result.Status.value__
|
||||
$item.is_valid = $result.IsValid
|
||||
$item.message_count = $result.MessageCount
|
||||
$item.last_error = $result.LastError
|
||||
$items.Add($item)
|
||||
}
|
||||
|
||||
$jsonString = $items | ConvertTo-JSON-20 -forceArray $true
|
||||
Write-Host $jsonString
|
||||
exit 0
|
||||
';
|
||||
|
||||
return $ps;
|
||||
}
|
||||
|
||||
sub check {
|
||||
my ($self, %options) = @_;
|
||||
# options: stdout
|
||||
|
||||
# Following output:
|
||||
#[identity= ][nexthopdomain= xxxx][deliverytype= SmtpRelayWithinAdSite][status= Active ][isvalid= Yes][messagecount= 1 ][[error=...]]
|
||||
$self->{output}->output_add(severity => 'OK',
|
||||
short_msg => "All Queues are ok.");
|
||||
|
||||
my $checked = 0;
|
||||
$self->{output}->output_add(long_msg => $options{stdout});
|
||||
|
||||
$self->{perfdatas_queues} = {};
|
||||
while ($options{stdout} =~ /\[identity=(.*?)\]\[nexthopdomain=(.*?)\]\[deliverytype=(.*?)\]\[status=(.*?)\]\[isvalid=(.*?)\]\[messagecount=(.*?)\]\[\[error=(.*?)\]\]/msg) {
|
||||
$self->{data} = {};
|
||||
($self->{data}->{identity}, $self->{data}->{nexthopdomain}, $self->{data}->{deliverytype}, $self->{data}->{status}, $self->{data}->{isvalid}, $self->{data}->{messagecount}, $self->{data}->{error}) =
|
||||
($self->{output}->to_utf8($1), centreon::plugins::misc::trim($2),
|
||||
centreon::plugins::misc::trim($3), centreon::plugins::misc::trim($4), centreon::plugins::misc::trim($5), centreon::plugins::misc::trim($6), centreon::plugins::misc::trim($7));
|
||||
|
||||
$checked++;
|
||||
|
||||
my ($status, $message) = ('ok');
|
||||
eval {
|
||||
local $SIG{__WARN__} = sub { $message = $_[0]; };
|
||||
local $SIG{__DIE__} = sub { $message = $_[0]; };
|
||||
|
||||
if (defined($self->{option_results}->{critical}) && $self->{option_results}->{critical} ne '' &&
|
||||
eval "$self->{option_results}->{critical}") {
|
||||
$status = 'critical';
|
||||
} elsif (defined($self->{option_results}->{warning}) && $self->{option_results}->{warning} ne '' &&
|
||||
eval "$self->{option_results}->{warning}") {
|
||||
$status = 'warning';
|
||||
}
|
||||
};
|
||||
if (defined($message)) {
|
||||
$self->{output}->output_add(long_msg => 'filter status issue: ' . $message);
|
||||
}
|
||||
if (!$self->{output}->is_status(value => $status, compare => 'ok', litteral => 1)) {
|
||||
$self->{output}->output_add(severity => $status,
|
||||
short_msg => sprintf("Queue '%s' status is '%s' [last error: %s]",
|
||||
$self->{data}->{nexthopdomain}, $self->{data}->{status}, $self->{data}->{error}));
|
||||
}
|
||||
|
||||
if ($self->{data}->{messagecount} =~ /^(\d+)/) {
|
||||
my $num = $1;
|
||||
my $identity = $self->{data}->{identity};
|
||||
|
||||
$identity = $1 if ($self->{data}->{identity} =~ /^(.*\\)[0-9]+$/);
|
||||
$self->{perfdatas_queues}->{$identity} = 0 if (!defined($self->{perfdatas_queues}->{$identity}));
|
||||
$self->{perfdatas_queues}->{$identity} += $num;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (keys %{$self->{perfdatas_queues}}) {
|
||||
$self->{output}->perfdata_add(label => 'queue_length_' . $_,
|
||||
value => $self->{perfdatas_queues}->{$_},
|
||||
min => 0);
|
||||
}
|
||||
|
||||
if ($checked == 0) {
|
||||
$self->{output}->output_add(severity => 'UNKNOWN',
|
||||
short_msg => 'Cannot find informations');
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
|
|
@ -67,7 +67,7 @@ function ConvertTo-JSON-20($maxDepth = 4,$forceArray = $false) {
|
|||
return "`"{0}`"" -f (Escape-JSONString $value )
|
||||
}
|
||||
'(System\.)?DateTime' {return "`"{0:yyyy-MM-dd}T{0:HH:mm:ss}`"" -f $value}
|
||||
'Int32|Double' {return "$value"}
|
||||
'Int16|Int32|Double' {return "$value"}
|
||||
'Boolean' {return "$value".ToLower()}
|
||||
'(System\.)?Object\[\]' { # array
|
||||
|
||||
|
|
Loading…
Reference in New Issue