(plugin) os::windows::snmp - refactor service mode (#4573)

Co-authored-by: Colin Gagnaire <gagnaire.colin@gmail.com>
This commit is contained in:
qgarnier 2023-08-08 15:29:31 +02:00 committed by GitHub
parent 7e011ac95b
commit 77a7fd9e84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -20,35 +20,99 @@
package os::windows::snmp::mode::service; package os::windows::snmp::mode::service;
use base qw(centreon::plugins::mode); use base qw(centreon::plugins::templates::counter);
use strict; use strict;
use warnings; use warnings;
use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng);
my %map_installed_state = ( sub custom_status_output {
1 => 'uninstalled', my ($self, %options) = @_;
2 => 'install-pending',
3 => 'uninstall-pending', my $msg = "state is '" . $self->{result_values}->{operating_state};
4 => 'installed' $msg .= "' [installed state: '" . $self->{result_values}->{installed_state} . "']";
); return $msg;
my %map_operating_state = ( }
1 => 'active',
2 => 'continue-pending', sub prefix_services_output {
3 => 'pause-pending', my ($self, %options) = @_;
4 => 'paused'
); return "Service '" . $options{instance_value}->{name} . "' ";
}
sub prefix_global_output {
my ($self, %options) = @_;
return 'Number of services ';
}
sub set_counters {
my ($self, %options) = @_;
$self->{maps_counters_type} = [
{ name => 'global', type => 0, cb_prefix_output => 'prefix_global_output' },
{ name => 'services', type => 1, cb_prefix_output => 'prefix_services_output',
message_multiple => 'All services are ok', skipped_code => { -10 => 1, -11 => 1 } }
];
$self->{maps_counters}->{global} = [
{ label => 'active', nlabel => 'services.active.count', display_ok => 0, set => {
key_values => [ { name => 'active' } ],
output_template => 'active: %d',
perfdatas => [
{ template => '%d', min => 0 }
]
}
},
{ label => 'continue-pending', nlabel => 'services.continue.pending.count', display_ok => 0, set => {
key_values => [ { name => 'continue-pending' } ],
output_template => 'continue pending: %d',
perfdatas => [
{ template => '%d', min => 0 }
]
}
},
{ label => 'pause-pending', nlabel => 'services.pause.pending.count', display_ok => 0, set => {
key_values => [ { name => 'pause-pending' } ],
output_template => 'pause pending: %d',
perfdatas => [
{ template => '%d', min => 0 }
]
}
},
{ label => 'paused', nlabel => 'services.paused.count', display_ok => 0, set => {
key_values => [ { name => 'paused' } ],
output_template => 'Service paused: %d',
perfdatas => [
{ template => '%d', min => 0 }
]
}
}
];
$self->{maps_counters}->{services} = [
{ label => 'status', type => 2, set => {
key_values => [ { name => 'operating_state' }, { name => 'installed_state' }, { name => 'name' } ],
closure_custom_output => $self->can('custom_status_output'),
closure_custom_perfdata => sub { return 0; },
closure_custom_threshold_check => \&catalog_status_threshold_ng
}
}
];
}
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 => {
'warning:s' => { name => 'warning', }, 'filter-name:s' => { name => 'filter_name' },
'critical:s' => { name => 'critical', }, 'warning:s' => { name => 'warning' }, # deprecated
'service:s@' => { name => 'service', }, 'critical:s' => { name => 'critical' }, # deprecated
'regexp' => { name => 'use_regexp', }, 'service:s@' => { name => 'service' }, # deprecated
'state:s' => { name => 'state' } 'regexp' => { name => 'use_regexp' }, # deprecated
'state:s' => { name => 'state' } # deprecated
}); });
return $self; return $self;
@ -56,117 +120,112 @@ sub new {
sub check_options { sub check_options {
my ($self, %options) = @_; my ($self, %options) = @_;
$self->SUPER::init(%options);
# Compatibility for deprecated options
if (defined($options{option_results}->{warning}) && $options{option_results}->{warning} ne '') {
$options{option_results}->{'warning-service-active-count'} = $options{option_results}->{warning};
}
if (defined($options{option_results}->{critical}) && $options{option_results}->{critical} ne '') {
$options{option_results}->{'critical-service-active-count'} = $options{option_results}->{critical};
}
if (!defined($self->{option_results}->{service})) { my $delimiter = '';
$self->{output}->add_option_msg(short_msg => "Need to specify at least one '--service' option."); if (defined($options{option_results}->{service})) {
$self->{output}->option_exit(); my $filter = '';
for my $filter_service (@{$options{option_results}->{service}}) {
next if ($filter_service eq '');
if (defined($options{option_results}->{use_regexp})) {
$filter.= $delimiter . $filter_service;
} else {
$filter .= $delimiter . quotemeta($filter_service);
}
$delimiter = '|';
}
if ($filter ne '' && !defined($options{option_results}->{use_regexp})) {
$filter = '^(' . $filter . ')$';
}
if (!defined($options{option_results}->{'filter_name'}) || $options{option_results}->{'filter_name'} eq '') {
$options{option_results}->{'filter_name'} = $filter;
}
}
if (defined($options{option_results}->{state}) && $options{option_results}->{state} ne '') {
$options{option_results}->{'critical-status'} = "%{operating_state} !~ /$options{option_results}->{state}/";
} }
if (($self->{perfdata}->threshold_validate(label => 'warning', value => $self->{option_results}->{warning})) == 0) { $self->SUPER::check_options(%options);
$self->{output}->add_option_msg(short_msg => "Wrong warning threshold '" . $self->{option_results}->{warning} . "'.");
$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) = @_;
my $map_installed_state = {
1 => 'uninstalled',
2 => 'install-pending',
3 => 'uninstall-pending',
4 => 'installed'
};
my $oid_svSvcEntry = '.1.3.6.1.4.1.77.1.2.3.1'; my $map_operating_state = {
my $oid_svSvcInstalledState = '.1.3.6.1.4.1.77.1.2.3.1.2'; 1 => 'active',
my $oid_svSvcOperatingState = '.1.3.6.1.4.1.77.1.2.3.1.3'; 2 => 'continue-pending',
my $result = $options{snmp}->get_table(oid => $oid_svSvcEntry, start => $oid_svSvcInstalledState, end => $oid_svSvcOperatingState); 3 => 'pause-pending',
4 => 'paused'
my $services_match = {}; };
$self->{output}->output_add(
severity => 'OK', my $mapping = {
short_msg => 'All service states are ok' installed_state => { oid => '.1.3.6.1.4.1.77.1.2.3.1.2', map => $map_installed_state }, # svSvcInstalledState
operating_state => { oid => '.1.3.6.1.4.1.77.1.2.3.1.3', map => $map_operating_state } # svSvcOperatingState
};
my $table_svSvcEntry = '.1.3.6.1.4.1.77.1.2.3.1';
my $snmp_result = $options{snmp}->get_table(
oid => $table_svSvcEntry,
start => $mapping->{installed_state}->{oid},
end => $mapping->{operating_state}->{oid},
nothing_quit => 1
); );
use Encode;
foreach my $oid ($options{snmp}->oid_lex_sort(keys %$result)) { $self->{global} = {
next if ($oid !~ /^$oid_svSvcOperatingState\.(\d+)\.(.*)$/); 'active' => 0,
'continue-pending' => 0,
'pause-pending' => 0,
'paused' => 0
};
$self->{services} = {};
foreach my $oid ($options{snmp}->oid_lex_sort(keys %$snmp_result)) {
next if ($oid !~ /^$mapping->{operating_state}->{oid}\.(\d+)\.(.*)$/);
my $instance = $1 . '.' . $2; my $instance = $1 . '.' . $2;
my $svc_name = $self->{output}->decode(join('', map(chr($_), split(/\./, $2)))); my $name = $self->{output}->decode(join('', map(chr($_), split(/\./, $2))));
my $svc_installed_state = $result->{$oid_svSvcInstalledState . '.' . $instance};
my $svc_operating_state = $result->{$oid_svSvcOperatingState . '.' . $instance};
for (my $i = 0; $i < scalar(@{$self->{option_results}->{service}}); $i++) {
$services_match->{$i} = {} if (!defined($services_match->{$i}));
my $filter = $self->{option_results}->{service}->[$i];
if (defined($self->{option_results}->{use_regexp}) && $svc_name =~ /$filter/) {
$services_match->{$i}->{$svc_name} = {
operating_state => $svc_operating_state,
installed_state => $svc_installed_state
}
} elsif ($svc_name eq $filter) {
$services_match->{$i}->{$svc_name} = {
operating_state => $svc_operating_state,
installed_state => $svc_installed_state
}
}
}
}
for (my $i = 0; $i < scalar(@{$self->{option_results}->{service}}); $i++) {
my $numbers = 0;
my $svc_name_state_wrong = {};
foreach my $svc_name (keys %{$services_match->{$i}}) {
my $operating_state = $services_match->{$i}->{$svc_name}->{operating_state};
my $installed_state = $services_match->{$i}->{$svc_name}->{installed_state};
$self->{output}->output_add(long_msg =>
sprintf(
"Service '%s' match (pattern: '%s') [operating state = %s, installed state = %s]",
$svc_name, $self->{option_results}->{service}->[$i],
$map_operating_state{$operating_state},
$map_installed_state{$installed_state}
)
);
if (defined($self->{option_results}->{state}) && $map_operating_state{$operating_state} !~ /$self->{option_results}->{state}/) {
delete $services_match->{$i}->{$svc_name};
$svc_name_state_wrong->{$svc_name} = $operating_state;
next;
}
$numbers++;
}
my $exit = $self->{perfdata}->threshold_check( next if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' &&
value => $numbers, threshold => [ $name !~ /$self->{option_results}->{filter_name}/);
{ label => 'critical', exit_litteral => 'critical' },
{ label => 'warning', exit_litteral => 'warning' } my $result = $options{snmp}->map_instance(
] mapping => $mapping,
results => $snmp_result,
instance => $instance
); );
$self->{output}->output_add(
long_msg => sprintf( $self->{services}->{$instance} = {
"Service pattern '%s': service list %s", name => $name,
$self->{option_results}->{service}->[$i], operating_state => $result->{operating_state},
join(', ', keys %{$services_match->{$i}}) installed_state => $result->{installed_state}
) };
);
if (!$self->{output}->is_status(value => $exit, compare => 'ok', litteral => 1)) { $self->{global}->{ $result->{operating_state} }++;
if (scalar(keys %$svc_name_state_wrong) > 0) {
$self->{output}->output_add(
severity => $exit,
short_msg => sprintf(
"Service pattern '%s' problem: %s [following services match but has the wrong state]",
$self->{option_results}->{service}->[$i],
join(', ', keys %$svc_name_state_wrong)
)
);
} else {
$self->{output}->output_add(
severity => $exit,
short_msg => sprintf("Service problem '%s'", $self->{option_results}->{service}->[$i])
);
}
}
} }
$self->{output}->display(); if (scalar(keys %{$self->{services}}) <= 0) {
$self->{output}->exit(); $self->{output}->add_option_msg(short_msg => "No service found.");
$self->{output}->option_exit();
}
} }
1; 1;
@ -175,29 +234,44 @@ __END__
=head1 MODE =head1 MODE
Check Windows Services in SNMP Check Windows services states
=over 8 =over 8
=item B<--filter-name>
Filter by service name (can be a regexp).
=item B<--warning-status> B<--critical-status>
Set WARNING or CRITICAL threshold for status.
You can use the following variables: %{operating_state}, %{installed_state}.
=item B<--warning-*> B<--critical-*>
Thresholds on services count.
Can be: 'active', 'continue-pending',
'pause-pending', 'paused'.
=item B<--warning> =item B<--warning>
Warning threshold. DEPRECATED. Use --warning-active instead.
=item B<--critical> =item B<--critical>
Critical threshold. DEPRECATED. Use --critical-active instead.
=item B<--service> =item B<--service>
Services to check. (can set multiple times) DEPRECATED. Use --filter-name instead.
=item B<--regexp> =item B<--regexp>
Allows to use regexp to filter services. DEPRECATED. Use --filter-name instead.
=item B<--state> =item B<--state>
Service state. (Regexp allowed) DEPRECATED. Use --critical/warning-status instead.
=back =back