diff --git a/src/network/cambium/cnmatrix/snmp/mode/components/psu.pm b/src/network/cambium/cnmatrix/snmp/mode/components/psu.pm new file mode 100644 index 0000000000..e18aff83be --- /dev/null +++ b/src/network/cambium/cnmatrix/snmp/mode/components/psu.pm @@ -0,0 +1,77 @@ +# +# Copyright 2026 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 network::cambium::cnmatrix::snmp::mode::components::psu; + +use strict; +use warnings; + +my %map_psu_status = ( + 1 => 'ok', + 2 => 'error', +); + +my $mapping = { + cnInternalPowerSupplyStatus => { oid => '.1.3.6.1.4.1.17713.24.4.30.1.2', map => \%map_psu_status }, +}; +my $oid_cnInternalPowerSupplyTableEntry = '.1.3.6.1.4.1.17713.24.4.30.1'; + +sub load { + my ($self) = @_; + + push @{$self->{request}}, { oid => $oid_cnInternalPowerSupplyTableEntry }; +} + +sub check { + my ($self) = @_; + + $self->{output}->output_add(long_msg => "Checking power supplies"); + $self->{components}->{psu} = {name => 'power supplies', total => 0, skip => 0}; + return if ($self->check_filter(section => 'psu')); + + foreach my $oid ($self->{snmp}->oid_lex_sort(keys %{$self->{results}->{$oid_cnInternalPowerSupplyTableEntry}})) { + next if ($oid !~ /^$mapping->{ cnInternalPowerSupplyStatus}->{oid}\.(.*)$/); + my $instance = $1; + my $result = $self->{snmp}->map_instance( + mapping => $mapping, + results => $self->{results}->{$oid_cnInternalPowerSupplyTableEntry}, + instance => $instance + ); + + next if ($self->check_filter(section => 'psu', instance => $instance)); + $self->{components}->{psu}->{total}++; + + $self->{output}->output_add( + long_msg => sprintf("Power supply '%s' status is %s [instance: %s].", + $instance, $result->{ cnInternalPowerSupplyStatus}, + $instance + )); + my $exit = $self->get_severity(section => 'psu', value => $result->{ cnInternalPowerSupplyStatus}); + if (!$self->{output}->is_status(value => $exit, compare => 'ok', litteral => 1)) { + $self->{output}->output_add( + severity => $exit, + short_msg => sprintf("Power supply '%s' status is %s", + $instance, $result->{ cnInternalPowerSupplyStatus} + )); + } + } +} + +1; \ No newline at end of file diff --git a/src/network/cambium/cnmatrix/snmp/mode/hardware.pm b/src/network/cambium/cnmatrix/snmp/mode/hardware.pm new file mode 100644 index 0000000000..1fc767f21d --- /dev/null +++ b/src/network/cambium/cnmatrix/snmp/mode/hardware.pm @@ -0,0 +1,107 @@ +# +# Copyright 2026 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 network::cambium::cnmatrix::snmp::mode::hardware; + +use base qw(centreon::plugins::templates::hardware); + +use strict; +use warnings; + +sub set_system { + my ($self, %options) = @_; + + $self->{regexp_threshold_numeric_check_section_option} = '^psu$'; + + $self->{cb_hook2} = 'snmp_execute'; + + $self->{thresholds} = { + psu => [ + ['ok', 'OK'], + ['error', 'CRITICAL'], + ], + }; + + $self->{components_path} = 'network::cambium::cnmatrix::snmp::mode::components'; + $self->{components_module} = [ 'psu']; +} + +sub snmp_execute { + my ($self, %options) = @_; + + $self->{snmp} = $options{snmp}; + $self->{results} = $self->{snmp}->get_multiple_table(oids => $self->{request}); +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options, no_performance => 1); + bless $self, $class; + + $options{options}->add_options(arguments => {}); + + return $self; +} + +1; + +__END__ + +=head1 MODE + +Check Hardware (Power Supplies). + +=over 8 + +=item B<--component> + +Which component to check (default: '.*'). +Can be: 'psu'. + +=item B<--filter> + +Exclude the items given as a comma-separated list (example: --filter=psu). +You can also exclude items from specific instances: --filter=psu,1 + +=item B<--absent-problem> + +Return an error if an entity is not 'present' (default is skipping) (comma separated list) +Can be specific or global: --absent-problem=psu,1 + +=item B<--no-component> + +Define the expected status if no components are found (default: critical). + +=item B<--threshold-overload> + +Use this option to override the status returned by the plugin when the status label matches a regular expression (syntax: section,[instance,]status,regexp). +Example: --threshold-overload='psu,CRITICAL,^(?!(ok)$)' + +=item B<--warning-count-*> + +Define the warning threshold for the number of components of one type (replace '*' with the component type). + +=item B<--critical-count-*> + +Define the critical threshold for the number of components of one type (replace '*' with the component type). + +=back + +=cut diff --git a/src/network/cambium/cnmatrix/snmp/mode/interfaces.pm b/src/network/cambium/cnmatrix/snmp/mode/interfaces.pm new file mode 100644 index 0000000000..baf897b812 --- /dev/null +++ b/src/network/cambium/cnmatrix/snmp/mode/interfaces.pm @@ -0,0 +1,328 @@ +# +# Copyright 2026 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 network::cambium::cnmatrix::snmp::mode::interfaces; + +use base qw(snmp_standard::mode::interfaces); + +use strict; +use warnings; + +sub set_counters_errors { + my ($self, %options) = @_; + + $self->SUPER::set_counters_errors(%options); + + push @{$self->{maps_counters}->{int}}, + { label => 'input-power', filter => 'add_optical', nlabel => 'interface.input.power.dbm', set => { + key_values => [ { name => 'input_power' }, { name => 'display' } ], + output_template => 'Input Power : %s dBm', + perfdatas => [ + { template => '%s', unit => 'dBm', label_extra_instance => 1, instance_use => 'display' } + ] + } + }, + { label => 'bias-current', filter => 'add_optical', nlabel => 'interface.bias.current.milliampere', set => { + key_values => [ { name => 'bias_current' }, { name => 'display' } ], + output_template => 'Bias Current : %s mA', + perfdatas => [ + { template => '%s', unit => 'mA', label_extra_instance => 1, instance_use => 'display' } + ] + } + }, + { label => 'output-power', filter => 'add_optical', nlabel => 'interface.output.power.dbm', set => { + key_values => [ { name => 'output_power' }, { name => 'display' } ], + output_template => 'Output Power : %s dBm', + perfdatas => [ + { template => '%s', unit => 'dBm', label_extra_instance => 1, instance_use => 'display' } + ] + } + }, + { label => 'voltage', filter => 'add_optical', nlabel => 'interface.voltage.volt', set => { + key_values => [ { name => 'voltage' }, { name => 'display' } ], + output_template => 'Voltage : %s V', + perfdatas => [ + { template => '%s', unit => 'V', label_extra_instance => 1, instance_use => 'display' } + ] + } + }, + { + label => 'module-temperature', + filter => 'add_optical', + nlabel => 'interface.module.temperature.celsius', + set => + { + key_values => [ { name => 'module_temperature' }, { name => 'display' } ], + output_template => 'Module Temperature : %.2f C', + perfdatas => [ + { template => '%.2f', unit => 'C', label_extra_instance => 1, instance_use => 'display' } + ] + } + } + ; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1); + bless $self, $class; + + $options{options}->add_options(arguments => { 'add-optical' => { name => 'add_optical' } }); + + return $self; +} + +sub skip_interface { + my ($self, %options) = @_; + + return ($self->{checking} =~ /cast|errors|traffic|status|volume|optical/ ? 0 : 1); +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + $self->{checking} = ''; + foreach ('add_global', + 'add_status', + 'add_errors', + 'add_traffic', + 'add_cast', + 'add_speed', + 'add_volume', + 'add_optical') { + if (defined($self->{option_results}->{$_})) { + $self->{checking} .= $_; + } + } +} + +sub reload_cache_custom { + my ($self, %options) = @_; + + $options{datas}->{optical_ports} = {}; + my $oid_cnTransceiverVendorName = '.1.3.6.1.4.1.2076.81.18.1.1.11.1.5'; + my $snmp_result = $self->{snmp}->get_table(oid => $oid_cnTransceiverVendorName); + + foreach (keys %$snmp_result) { + next if (!/^$oid_cnTransceiverVendorName\.(\d+)$/); + $options{datas}->{optical_ports}->{$1} = $snmp_result->{$_}; + } +} + +my $mapping_optical = { + output_power => { oid => '.1.3.6.1.4.1.2076.81.18.1.1.11.1.14' },# cnTransceiverTxPower + input_power => { oid => '.1.3.6.1.4.1.2076.81.18.1.1.11.1.15' },# cnTransceiverRxPower + module_temperature => { oid => '.1.3.6.1.4.1.2076.81.18.1.1.11.1.11' },# cnTransceiverTemperature + voltage => { oid => '.1.3.6.1.4.1.2076.81.18.1.1.11.1.12' },# cnTransceiverVoltage + bias_current => { oid => '.1.3.6.1.4.1.2076.81.18.1.1.11.1.13' }# cnTransceiverTxBias +}; + +sub custom_load { + my ($self, %options) = @_; + + return if (!defined($self->{option_results}->{add_optical})); + + my $optical_ports = $self->{statefile_cache}->get(name => 'optical_ports'); + $self->{snmp}->load( + oids => [ map($_->{oid}, values(%$mapping_optical)) ], + instances => [ keys %$optical_ports ] + ); +} + +sub custom_add_result { + my ($self, %options) = @_; + + return if (!defined($self->{option_results}->{add_optical})); + + my $optical_ports = $self->{statefile_cache}->get(name => 'optical_ports'); + return if (!defined($optical_ports->{ $options{instance} })); + + my $result = $self->{snmp}->map_instance( + mapping => $mapping_optical, + results => $self->{results}, + instance => $options{instance} + ); + + $self->{int}->{ $options{instance} }->{input_power} = undef; + if (defined($result->{input_power}) && $result->{input_power} != 0 && $result->{input_power} != -32768) { + $self->{int}->{ $options{instance} }->{input_power} = $result->{input_power} / 100; + } + + $self->{int}->{$options{instance}}->{bias_current} = undef; + if (defined($result->{bias_current}) && $result->{bias_current} != 0 && $result->{bias_current} != -32768) { + $self->{int}->{$options{instance}}->{bias_current} = $result->{bias_current} / 100; + } + + $self->{int}->{$options{instance}}->{output_power} = undef; + if (defined($result->{output_power}) && $result->{output_power} != 0 && $result->{output_power} != -32768) { + $self->{int}->{$options{instance}}->{output_power} = $result->{output_power} / 100; + } + + $self->{int}->{$options{instance}}->{module_temperature} = undef; + if (defined($result->{module_temperature}) && $result->{module_temperature} != 0 && $result->{module_temperature} != -32768) { + $self->{int}->{$options{instance}}->{module_temperature} = $result->{module_temperature}; + } + + $self->{int}->{$options{instance}}->{voltage} = undef; + if (defined($result->{voltage}) && $result->{voltage} != 0 && $result->{voltage} != -32768) { + $self->{int}->{$options{instance}}->{voltage} = $result->{voltage} / 100; + } +} + +1; + +__END__ + +=head1 MODE + +Check interfaces. + +=over 8 + +=item B<--add-global> + +Check global port statistics (by default if no --add-* option is set). + +=item B<--add-status> + +Check interface status. + +=item B<--add-duplex-status> + +Check duplex status (with --warning-status and --critical-status). + +=item B<--add-traffic> + +Check interface traffic. + +=item B<--add-errors> + +Check interface errors. + +=item B<--add-cast> + +Check interface cast. + +=item B<--add-speed> + +Check interface speed. + +=item B<--add-volume> + +Check interface data volume between two checks (not supposed to be graphed, useful for BI reporting). + +=item B<--add-optical> + +Check interface optical metrics. + +=item B<--check-metrics> + +If the expression is true, metrics are checked (default: '%{opstatus} eq "up"'). + +=item B<--warning-status> + +Define the conditions to match for the status to be WARNING. +You can use the following variables: %{admstatus}, %{opstatus}, %{duplexstatus}, %{display} + +=item B<--critical-status> + +Define the conditions to match for the status to be CRITICAL (default: '%{admstatus} eq "up" and %{opstatus} ne "up"'). +You can use the following variables: %{admstatus}, %{opstatus}, %{duplexstatus}, %{display} + +=item B<--warning-*> B<--critical-*> + +Thresholds. +Can be: 'total-port', 'total-admin-up', 'total-admin-down', 'total-oper-up', 'total-oper-down', +'in-traffic', 'out-traffic', 'in-error', 'in-discard', 'out-error', 'out-discard', +'in-ucast', 'in-bcast', 'in-mcast', 'out-ucast', 'out-bcast', 'out-mcast', +'speed' (b/s). + +And also: 'input-power' (dBm), 'bias-current' (mA), 'output-power' (dBm), 'voltage' (mV), 'module-temperature' (C). + +=item B<--units-traffic> + +Units of thresholds for the traffic (default: 'percent_delta') ('percent_delta', 'bps', 'counter'). + +=item B<--units-errors> + +Units of thresholds for errors/discards (default: 'percent_delta') ('percent_delta', 'percent', 'delta', 'deltaps', 'counter'). + +=item B<--units-cast> + +Units of thresholds for communication types (default: 'percent_delta') ('percent_delta', 'percent', 'delta', 'deltaps', 'counter'). + +=item B<--nagvis-perfdata> + +Display traffic perfdata to be compatible with C widget. + +=item B<--interface> + +Set the interface (number expected) example: 1,2,... (empty means 'check all interfaces'). + +=item B<--name> + +Allows you to define the interface (in option --interface) by name instead of OID index. The name matching mode supports regular expressions. + +=item B<--speed> + +Set interface speed for incoming/outgoing traffic (in Mb). + +=item B<--speed-in> + +Set interface speed for incoming traffic (in Mb). + +=item B<--speed-out> + +Set interface speed for outgoing traffic (in Mb). + +=item B<--force-counters32> + +Force to use 32 bits counters (even in snmp v2c and v3). Should be used when 64 bits counters are buggy. + +=item B<--reload-cache-time> + +Time in minutes before reloading cache file (default: 180). + +=item B<--oid-filter> + +Define the OID to be used to filter interfaces (default: ifName) (values: ifDesc, ifAlias, ifName, IpAddr). + +=item B<--oid-display> + +Define the OID that will be used to name the interfaces (default: ifName) (values: ifDesc, ifAlias, ifName, IpAddr). + +=item B<--oid-extra-display> + +Add an OID to display. + +=item B<--display-transform-src> B<--display-transform-dst> + +Modify the interface name displayed by using a regular expression. + +Example: adding --display-transform-src='eth' --display-transform-dst='ens' will replace all occurrences of 'eth' with 'ens' + +=item B<--show-cache> + +Display cache interface data. + +=back + +=cut diff --git a/src/network/cambium/cnmatrix/snmp/mode/listsfpport.pm b/src/network/cambium/cnmatrix/snmp/mode/listsfpport.pm new file mode 100644 index 0000000000..f27832b473 --- /dev/null +++ b/src/network/cambium/cnmatrix/snmp/mode/listsfpport.pm @@ -0,0 +1,191 @@ +# +# Copyright 2026 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 network::cambium::cnmatrix::snmp::mode::listsfpport; + +use base qw(centreon::plugins::mode); + +use strict; +use warnings; +use centreon::plugins::misc qw/is_excluded/; + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $options{options}->add_options( + arguments => { + 'add-interface-name' => { name => 'add_interface_name' }, + 'include-type:s' => { name => 'include_type' }, + 'exclude-type:s' => { name => 'exclude_type' } + }); + + return $self; +} + +my @labels = ('number', 'vendor', 'vendor_oui', 'vendor_part_no', 'serial', 'type'); + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::init(%options); + + if (defined($self->{option_results}->{add_interface_name}) || defined($self->{option_results}->{add_interface_name})) { + push @labels, 'interface_name'; + } +} + +sub manage_selection { + my ($self, %options) = @_; + + my $map_type = { + 1 => 'cn1000BASE-T', + 2 => 'cn1000BASE-CX', + 3 => 'cn1000BASE-LX', + 4 => 'cn1000BASE-SX', + 5 => 'cn10GBASE-SR', + 6 => 'cn10GBASE-LR', + 7 => 'cn10GBASE-ER', + 8 => 'cn10GBASE-LRM', + 9 => 'cn10GBASE-SW', + 10 => 'cn10GBASE-LW', + 11 => 'cn10GBASE-EW' + }; + + # Select relevant oids for discovery function + my $mapping = { + type => { oid => '.1.3.6.1.4.1.2076.81.18.1.1.11.1.3', map => $map_type },# cnTransceiverType + vendor => { oid => '.1.3.6.1.4.1.2076.81.18.1.1.11.1.5' },# cnTransceiverVendorName + vendorOUI => { oid => '.1.3.6.1.4.1.2076.81.18.1.1.11.1.6' },# cnTransceiverVendorOUI + vendorPartNo => { oid => '.1.3.6.1.4.1.2076.81.18.1.1.11.1.7' },# cnTransceiverVendorPartNo + serial => { oid => '.1.3.6.1.4.1.2076.81.18.1.1.11.1.9' },# cnTransceiverVendorSerial + }; + + my $snmp_result = $options{snmp}->get_multiple_table( + oids => [ + { oid => $mapping->{type}->{oid} }, + { oid => $mapping->{vendor}->{oid} }, + { oid => $mapping->{vendorOUI}->{oid} }, + { oid => $mapping->{vendorPartNo}->{oid} }, + { oid => $mapping->{serial}->{oid} }, + ], + return_type => 1, nothing_quit => 1 + ); + + my $results = {}; + my $names = undef; + my $oid_name = '.1.3.6.1.2.1.31.1.1.1.1'; + + if (defined($self->{option_results}->{add_interface_name}) || defined($self->{option_results}->{add_interface_name})) { + $names = $options{snmp}->get_table( + oid => $oid_name, + nothing_quit => 1 + ); + } + + foreach my $oid (keys %{$snmp_result}) { + next if ($oid !~ /^$mapping->{serial}->{oid}\.(.*)$/); + + my $instance = $1; + my $result = $options{snmp}->map_instance(mapping => $mapping, results => $snmp_result, instance => $instance); + + next if is_excluded( + $result->{type}, + $self->{option_results}->{include_type}, + $self->{option_results}->{exclude_type}, + output => $self->{output} + ); + + my $oui = join '-', map {sprintf "%02X", ord($_)} split //, $result->{vendorOUI}; + + $results->{$instance} = { + number => $instance, + vendor => ($result->{vendor} =~ s/^\s+|\s+$//gr), + vendor_oui => $oui, + vendor_part_no => ($result->{vendorPartNo} =~ s/^\s+|\s+$//gr), + serial => ($result->{serial} =~ s/^\s+|\s+$//gr), + type => $result->{type}, + interface_name => defined($names->{$oid_name . '.' . $instance}) ? + $names->{$oid_name . '.' . $instance} : '' + }; + } + + return $results; +} + +sub run { + my ($self, %options) = @_; + + my $results = $self->manage_selection(snmp => $options{snmp}); + foreach my $port (sort keys %$results) { + $self->{output}->output_add(long_msg => + join('', map("[$_: " . $results->{$port}->{$_} . ']', @labels)) + ); + } + + $self->{output}->output_add( + severity => 'OK', + short_msg => 'List SFP' + ); + $self->{output}->display(nolabel => 1, force_ignore_perfdata => 1, force_long_output => 1); + $self->{output}->exit(); +} + +sub disco_format { + my ($self, %options) = @_; + + $self->{output}->add_disco_format(elements => [ @labels ]); +} + +sub disco_show { + my ($self, %options) = @_; + + my $results = $self->manage_selection(snmp => $options{snmp}); + foreach (sort keys %$results) { + $self->{output}->add_disco_entry( + %{$results->{$_}} + ); + } +} + +1; + +__END__ + +=head1 MODE + +List C ports. + +=over 8 + +=item B<--add-interface-name> + +Add the corresponding interface name when set. + +=item B<--include-type> + +Filters the ports by the connector type. Can be: C, C, C, C, C, C, C, C, C + +=item B<--exclude-type> + +Filters the ports by the connector type. Can be: C, C, C, C, C, C, C, C, C + +=back + +=cut diff --git a/src/network/cambium/cnmatrix/snmp/mode/sfpport.pm b/src/network/cambium/cnmatrix/snmp/mode/sfpport.pm new file mode 100644 index 0000000000..151e2d57af --- /dev/null +++ b/src/network/cambium/cnmatrix/snmp/mode/sfpport.pm @@ -0,0 +1,432 @@ +# +# Copyright 2026 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 network::cambium::cnmatrix::snmp::mode::sfpport; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; +use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng); +use centreon::plugins::statefile; +use centreon::plugins::constants qw(:counters :values); +use centreon::plugins::misc qw/is_excluded/; +use Safe; + +sub prefix_sfp_output { + my ($self, %options) = @_; + + my $foo = sprintf( + "sfp port '%s'%s%s%s ", + $options{instance}, + $options{instance_value}->{interface} ne '' ? ' - ' . $options{instance_value}->{interface} : '', + $options{instance_value}->{serial} ne '' ? ' - serial: ' . $options{instance_value}->{serial} : '', + $options{instance_value}->{type} ne '' ? ' - type: ' . $options{instance_value}->{type} : '' + ); + + return $foo; +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { + name => 'sfp', + type => COUNTER_TYPE_INSTANCE, + cb_prefix_output => 'prefix_sfp_output', + message_multiple => 'All sfp ports are ok', + skipped_code => { NO_VALUE() => 1 } + } + ]; + + $self->{maps_counters}->{sfp} = [ + { label => 'input-power', nlabel => 'interface.input.power.dbm', display_ok => 0, set => { + key_values => [ { name => 'input_power' }, { name => 'display' } ], + output_template => 'Input Power : %s dBm', + perfdatas => [ + { template => '%s', unit => 'dBm', label_extra_instance => 1, instance_use => 'display' } + ] + } + }, + { label => 'bias-current', nlabel => 'interface.bias.current.milliampere', display_ok => 0, set => { + key_values => [ { name => 'bias_current' }, { name => 'display' } ], + output_template => 'Bias Current : %s mA', + perfdatas => [ + { template => '%s', unit => 'mA', label_extra_instance => 1, instance_use => 'display' } + ] + } + }, + { label => 'output-power', nlabel => 'interface.output.power.dbm', display_ok => 0, set => { + key_values => [ { name => 'output_power' }, { name => 'display' } ], + output_template => 'Output Power : %s dBm', + perfdatas => [ + { template => '%s', unit => 'dBm', label_extra_instance => 1, instance_use => 'display' } + ] + } + }, + { label => 'voltage', nlabel => 'interface.voltage.volt', display_ok => 0, set => { + key_values => [ { name => 'voltage' }, { name => 'display' } ], + output_template => 'Voltage : %s V', + perfdatas => [ + { template => '%s', unit => 'V', label_extra_instance => 1, instance_use => 'display' } + ] + } + }, + { label => 'module-temperature', nlabel => 'interface.module.temperature.celsius', display_ok => 0, set => + { + key_values => [ { name => 'module_temperature' }, { name => 'display' } ], + output_template => 'Module Temperature : %.2f C', + perfdatas => [ + { template => '%.2f', unit => 'C', label_extra_instance => 1, instance_use => 'display' } + ] + } + } + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1); + bless $self, $class; + + $options{options}->add_options( + arguments => { + 'include-port:s' => { name => 'include_port' }, + 'exclude-port:s' => { name => 'exclude_port' }, + 'include-serial:s' => { name => 'include_serial' }, + 'exclude-serial:s' => { name => 'exclude_serial' }, + 'include-interface:s' => { name => 'include_interface' }, + 'exclude-interface:s' => { name => 'exclude_interface' }, + 'add-interface-name' => { name => 'add_interface_name' }, + 'reload-cache-time:s' => { name => 'reload_cache_time', default => 180 }, + 'show-cache' => { name => 'show_cache' }, + 'display-transform-src:s' => { name => 'display_transform_src' }, + 'display-transform-dst:s' => { name => 'display_transform_dst' } + } + ); + $self->{statefile_cache} = centreon::plugins::statefile->new(%options); + + $self->{safe} = Safe->new(); + $self->{safe}->share('$assign_var'); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + + $self->SUPER::check_options(%options); + $self->{statefile_cache}->check_options(%options); +} + +my $map_type = { + 1 => 'cn1000BASE-T', + 2 => 'cn1000BASE-CX', + 3 => 'cn1000BASE-LX', + 4 => 'cn1000BASE-SX', + 5 => 'cn10GBASE-SR', + 6 => 'cn10GBASE-LR', + 7 => 'cn10GBASE-ER', + 8 => 'cn10GBASE-LRM', + 9 => 'cn10GBASE-SW', + 10 => 'cn10GBASE-LW', + 11 => 'cn10GBASE-EW' +}; + +my $mapping = { + serial => { oid => '.1.3.6.1.4.1.2076.81.18.1.1.11.1.9' },# cnTransceiverVendorSerial + type => { oid => '.1.3.6.1.4.1.2076.81.18.1.1.11.1.3', map => $map_type },# cnTransceiverType + outputPower => { oid => '.1.3.6.1.4.1.2076.81.18.1.1.11.1.14' },# cnTransceiverTxPower + inputPower => { oid => '.1.3.6.1.4.1.2076.81.18.1.1.11.1.15' },# cnTransceiverRxPower + biasCurrent => { oid => '.1.3.6.1.4.1.2076.81.18.1.1.11.1.13' },# cnTransceiverTxBias + voltage => { oid => '.1.3.6.1.4.1.2076.81.18.1.1.11.1.12' },# cnTransceiverVoltage + moduleTemperature => { oid => '.1.3.6.1.4.1.2076.81.18.1.1.11.1.11' },# cnTransceiverTemperature +}; + +sub reload_cache { + my ($self, %options) = @_; + my $datas = {}; + + $datas->{last_timestamp} = time(); + $datas->{sfp} = {}; + my $snmp_names = {}; + + if (defined($self->{option_results}->{add_interface_name}) || defined($self->{option_results}->{add_interface_name})) { + my $oid_interface_name = '.1.3.6.1.2.1.31.1.1.1.1'; + my $result = $options{snmp}->get_table( + oid => $oid_interface_name, + ); + + foreach my $key (keys %$result) { + next if $key !~ /^$oid_interface_name\.(.*)$/; + my $instance = $1; + + $snmp_names->{$instance} = $self->{output}->decode($result->{$key}); + } + } + + my $result = $options{snmp}->get_table( + oid => $mapping->{serial}->{oid}, + ); + + foreach my $key (keys %$result) { + next if ($key !~ /$mapping->{serial}->{oid}\.([0-9]+)$/); + my $instance = $1; + + $datas->{sfp}->{$instance} = [ + $self->{output}->decode($result->{$mapping->{serial}->{oid} . '.' . $instance}), + exists($snmp_names->{$instance}) ? $snmp_names->{$instance} : "" + ]; + } + + if (scalar(keys %{$datas->{sfp}}) <= 0) { + $self->{output}->option_exit(short_msg => "Can't construct cache..."); + } + + $self->{statefile_cache}->write(data => $datas); + return $datas->{sfp}; +} + +sub get_selection { + my ($self, %options) = @_; + + # init cache file + my $has_cache_file = $self->{statefile_cache}->read(statefile => + 'cache_snmpstandard_' . $options{snmp}->get_hostname() . '_' . $options{snmp}->get_port() . '_' . $self->{mode}); + if (defined($self->{option_results}->{show_cache})) { + $self->{output}->option_exit(long_msg => $self->{statefile_cache}->get_string_content()); + } + + my $timestamp_cache = $self->{statefile_cache}->get(name => 'last_timestamp'); + my $sfp_ports = $self->{statefile_cache}->get(name => 'sfp'); + if ($has_cache_file == 0 || !defined($timestamp_cache) || !defined($sfp_ports) || ((time() - $timestamp_cache) > (($self->{option_results}->{reload_cache_time}) * 60))) { + $sfp_ports = $self->reload_cache(snmp => $options{snmp}); + $self->{statefile_cache}->read(); + } + + my $results = {}; + foreach (keys %$sfp_ports) { + next if is_excluded( + $_, + $self->{option_results}->{include_port}, + $self->{option_results}->{exclude_port}, + output => $self->{output} + ); + next if is_excluded($sfp_ports->{$_}->[0], + $self->{option_results}->{include_serial}, + $self->{option_results}->{exclude_serial}, + output => $self->{output} + ); + next if is_excluded($sfp_ports->{$_}->[1], + $self->{option_results}->{include_interface}, + $self->{option_results}->{exclude_interface}, + output => $self->{output} + ); + + $results->{$_} = $sfp_ports->{$_}; + } + + if (scalar(keys %$results) <= 0) { + $self->{output}->option_exit(short_msg => "No sfp ports found. Can be: filters, cache file."); + } + + return $results; +} + +sub manage_selection { + my ($self, %options) = @_; + + my $sfp_ports = $self->get_selection(snmp => $options{snmp}); + + $options{snmp}->load( + oids => [ map($_->{oid}, values(%$mapping)) ], + instances => [ keys %$sfp_ports ], + nothing_quit => 1 + ); + my $snmp_result = $options{snmp}->get_leef(); + + $self->{sfp} = {}; + + foreach (keys %$sfp_ports) { + my $instance = $_; + + my $result = $options{snmp}->map_instance( + mapping => $mapping, + results => $snmp_result, + instance => $instance + ); + + my $display = defined($self->{option_results}->{add_interface_name}) ? + $instance . '-' . exists($sfp_ports->{$instance}->[1]) ? $sfp_ports->{$instance}->[1] : '' : $instance; + $display = $self->get_display_value(value => $display); + + $self->{sfp}->{$instance}->{interface} = defined($self->{option_results}->{add_interface_name}) && + exists($sfp_ports->{$instance}->[1]) ? + $sfp_ports->{$instance}->[1] : + ''; + + $self->{sfp}->{$instance}->{port} = $instance; + ($result->{serial}) =~ s/^\s+|\s+$//g; + $self->{sfp}->{$instance}->{serial} = $result->{serial}; + $self->{sfp}->{$instance}->{type} = $result->{type}; + + $self->{sfp}->{$instance}->{input_power} = undef; + if (defined($result->{inputPower}) && $result->{inputPower} != 0 && $result->{inputPower} != -32768) { + $self->{sfp}->{$instance}->{input_power} = $result->{inputPower} / 1000; + } + + $self->{sfp}->{$instance}->{output_power} = undef; + if (defined($result->{outputPower}) && $result->{outputPower} != 0 && $result->{outputPower} != -32768) { + $self->{sfp}->{$instance}->{output_power} = $result->{outputPower} / 1000; + } + + $self->{sfp}->{$instance}->{bias_current} = undef; + if (defined($result->{biasCurrent}) && $result->{biasCurrent} != 0 && $result->{biasCurrent} != -32768) { + $self->{sfp}->{$instance}->{bias_current} = $result->{biasCurrent} / 1000; + } + + $self->{sfp}->{$instance}->{voltage} = undef; + if (defined($result->{biasCurrent}) && $result->{voltage} != 0 && $result->{voltage} != -32768) { + $self->{sfp}->{$instance}->{voltage} = $result->{voltage} / 1000; + } + + $self->{sfp}->{$instance}->{module_temperature} = undef; + if (defined($result->{biasCurrent}) && $result->{moduleTemperature} != 0 && $result->{moduleTemperature} != -32768) { + $self->{sfp}->{$instance}->{module_temperature} = $result->{moduleTemperature}; + } + + $self->{sfp}->{$instance}->{display} = $display; + + } +} + +sub get_display_value { + my ($self, %options) = @_; + + our $assign_var = $options{value}; + if (defined($self->{option_results}->{display_transform_src})) { + $self->{option_results}->{display_transform_dst} = '' if (!defined($self->{option_results}->{display_transform_dst})); + + $self->{safe}->reval("\$assign_var =~ s{$self->{option_results}->{display_transform_src}}{$self->{option_results}->{display_transform_dst}}", + 1); + if ($@) { + die 'Unsafe code evaluation: ' . $@; + } + } + + return $assign_var; +} + +1; + +__END__ + +=head1 MODE + +Check C port. + +=over 8 + +=item B<--include-port> + +Filter ports by index (can be a regexp). + +=item B<--exclude-port> + +Exclude ports by index (can be a regexp). + +=item B<--include-serial> + +Filter ports by serial (can be a regexp). + +=item B<--exclude-serial> + +Exclude ports by serial (can be a regexp). + +=item B<--include-interface> + +Filter ports by interface name (can be a regexp). Can be used only together with --add-interface-name. + +=item B<--exclude-interface> + +Exclude ports by interface name (can be a regexp). Can be used only together with --add-interface-name. + +=item B<--add-interface-name> + +Add the corresponding interface name when set. Used for the instance name in performance data, too. + +=item B<--warning-input-power> + +Thresholds (dBm). + +=item B<--critical-input-power> + +Thresholds (dBm). + +=item B<--warning-output-power> + +Thresholds (dBm). + +=item B<--critical-output-power> + +Thresholds (dBm). + +=item B<--warning-bias-current> + +Thresholds (mA). + +=item B<--critical-bias-current> + +Thresholds (mA). + +=item B<--warning-voltage> + +Thresholds (V). + +=item B<--critical-voltage> + +Thresholds (V). + +=item B<--warning-module-temperature> + +Thresholds (C). + +=item B<--critical-module-temperature> + +Thresholds (C). + +=item B<--reload-cache-time> + +Time in minutes before reloading cache file (default: 180). + +=item B<--display-transform-src> B<--display-transform-dst> + +Modify the interface name displayed by using a regular expression. + +Example: adding C<--display-transform-src='eth' --display-transform-dst='ens'> will replace all occurrences of 'eth' with 'ens' + +=item B<--show-cache> + +Display cache interface data. + +=back + +=cut diff --git a/src/network/cambium/cnmatrix/snmp/plugin.pm b/src/network/cambium/cnmatrix/snmp/plugin.pm new file mode 100644 index 0000000000..a9cc407748 --- /dev/null +++ b/src/network/cambium/cnmatrix/snmp/plugin.pm @@ -0,0 +1,61 @@ +# +# Copyright 2026 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 network::cambium::cnmatrix::snmp::plugin; + +use strict; +use warnings; +use base qw(centreon::plugins::script_snmp); + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{modes} = { + 'arp' => 'snmp_standard::mode::arp', + 'hardware' => 'network::cambium::cnmatrix::snmp::mode::hardware', + 'interfaces' => 'network::cambium::cnmatrix::snmp::mode::interfaces', + 'list-interfaces' => 'snmp_standard::mode::listinterfaces', + 'list-sfp-port' => 'network::cambium::cnmatrix::snmp::mode::listsfpport', + 'sfp-port' => 'network::cambium::cnmatrix::snmp::mode::sfpport', + 'tcpcon' => 'snmp_standard::mode::tcpcon', + 'udpcon' => 'snmp_standard::mode::udpcon', + 'uptime' => 'snmp_standard::mode::uptime', + }; + + $self->{modes_options} = { + 'tcpcon' => { force_new_perfdata => 1 }, + 'udpcon' => { force_new_perfdata => 1 }, + 'uptime' => { force_new_perfdata => 1 } + }; + + return $self; +} + +1; + +__END__ + +=head1 PLUGIN DESCRIPTION + +Check Cambium C equipment through SNMP. + +=cut