diff --git a/packaging/centreon-plugin-Applications-Podman-Restapi/deb.json b/packaging/centreon-plugin-Applications-Podman-Restapi/deb.json new file mode 100644 index 0000000000..d1383e463f --- /dev/null +++ b/packaging/centreon-plugin-Applications-Podman-Restapi/deb.json @@ -0,0 +1,4 @@ +{ + "dependencies": [ + ] +} diff --git a/packaging/centreon-plugin-Applications-Podman-Restapi/pkg.json b/packaging/centreon-plugin-Applications-Podman-Restapi/pkg.json new file mode 100644 index 0000000000..9432bf6fbe --- /dev/null +++ b/packaging/centreon-plugin-Applications-Podman-Restapi/pkg.json @@ -0,0 +1,9 @@ +{ + "pkg_name": "centreon-plugin-Applications-Podman-Restapi", + "pkg_summary": "Centreon Plugin", + "plugin_name": "centreon_podman_restapi.pl", + "files": [ + "centreon/plugins/script_custom.pm", + "apps/podman/restapi/" + ] +} diff --git a/packaging/centreon-plugin-Applications-Podman-Restapi/rpm.json b/packaging/centreon-plugin-Applications-Podman-Restapi/rpm.json new file mode 100644 index 0000000000..d1383e463f --- /dev/null +++ b/packaging/centreon-plugin-Applications-Podman-Restapi/rpm.json @@ -0,0 +1,4 @@ +{ + "dependencies": [ + ] +} diff --git a/src/apps/podman/restapi/custom/api.pm b/src/apps/podman/restapi/custom/api.pm new file mode 100644 index 0000000000..2ae7eff74b --- /dev/null +++ b/src/apps/podman/restapi/custom/api.pm @@ -0,0 +1,322 @@ +# +# Copyright 2025 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::podman::restapi::custom::api; + +use strict; +use warnings; +use centreon::plugins::http; +use centreon::plugins::misc; +use JSON::XS; + +sub new { + my ($class, %options) = @_; + my $self = {}; + bless $self, $class; + + if (!defined($options{output})) { + print "Class Custom: Need to specify 'output' argument.\n"; + exit 3; + } + if (!defined($options{options})) { + $options{output}->add_option_msg(short_msg => "Class Custom: Need to specify 'options' argument."); + $options{output}->option_exit(); + } + + if (!defined($options{noptions})) { + $options{options}->add_options(arguments => { + 'hostname:s' => { name => 'hostname' }, + 'port:s' => { name => 'port' }, + 'proto:s' => { name => 'proto' }, + 'url-path:s' => { name => 'url_path' }, + 'timeout:s' => { name => 'timeout' } + }); + # curl --cacert /path/to/ca.crt --cert /path/to/podman.crt --key /path/to/podman.key https://localhost:8080/v5.0.0/libpod/info + # curl --unix-socket $XDG_RUNTIME_DIR/podman/podman.sock 'http://d/v5.0.0/libpod/pods/stats?namesOrIDs=blog' | jq + } + $options{options}->add_help(package => __PACKAGE__, sections => 'REST API OPTIONS', once => 1); + + $self->{output} = $options{output}; + $self->{http} = centreon::plugins::http->new(%options, default_backend => 'curl'); + + return $self; +} + +sub set_options { + my ($self, %options) = @_; + + $self->{option_results} = $options{option_results}; +} + +sub set_defaults {} + +sub check_options { + my ($self, %options) = @_; + + $self->{hostname} = (defined($self->{option_results}->{hostname})) ? $self->{option_results}->{hostname} : ''; + $self->{proto} = (defined($self->{option_results}->{proto})) ? $self->{option_results}->{proto} : 'https'; + $self->{port} = (defined($self->{option_results}->{port})) ? $self->{option_results}->{port} : 443; + $self->{url_path} = (defined($self->{option_results}->{url_path})) ? $self->{option_results}->{url_path} : '/v5.0.0/libpod/'; + $self->{timeout} = (defined($self->{option_results}->{timeout})) ? $self->{option_results}->{timeout} : 30; + + if ($self->{hostname} eq '') { + $self->{output}->add_option_msg(short_msg => 'Need to specify hostname option.'); + $self->{output}->option_exit(); + } + + $self->{http}->set_options(%{$self->{option_results}}); + + return 0; +} + +sub json_decode { + my ($self, %options) = @_; + + $options{content} =~ s/\r//mg; + my $decoded; + eval { + $decoded = JSON::XS->new->utf8->decode($options{content}); + }; + if ($@) { + $self->{output}->add_option_msg(short_msg => "Cannot decode json response: $@"); + $self->{output}->option_exit(); + } + + return $decoded; +} + +sub build_options_for_httplib { + my ($self, %options) = @_; + + $self->{option_results}->{hostname} = $self->{hostname}; + $self->{option_results}->{port} = $self->{port}; + $self->{option_results}->{proto} = $self->{proto}; + $self->{option_results}->{timeout} = $self->{timeout}; +} + +sub settings { + my ($self, %options) = @_; + + $self->build_options_for_httplib(); + $self->{http}->add_header(key => 'Accept', value => 'application/json'); + $self->{http}->set_options(%{$self->{option_results}}); +} + +sub request { + my ($self, %options) = @_; + + my $endpoint = $options{full_endpoint}; + if (!defined($endpoint)) { + $endpoint = $self->{url_path} . $options{endpoint}; + } + + $self->settings(); + + my $content = $self->{http}->request( + method => $options{method}, + url_path => $endpoint, + get_param => $options{get_param}, + header => [ + 'Accept: application/json' + ], + warning_status => '', + unknown_status => '', + critical_status => '' + ); + + my $decoded = $self->json_decode(content => $content); + if (!defined($decoded)) { + $self->{output}->add_option_msg(short_msg => 'Error while retrieving data (add --debug option for detailed message)'); + $self->{output}->option_exit(); + } + return $decoded; +} + +sub system_info { + my ($self, %options) = @_; + + my $results = $self->request( + endpoint => 'info', + method => 'GET' + ); + + return $results; +} + +sub list_containers { + my ($self, %options) = @_; + + my $results = $self->request( + endpoint => 'containers/json', + method => 'GET' + ); + + my $containers = {}; + foreach my $container (@{$results}) { + $containers->{$container->{Id}} = { + Name => $container->{Names}->[0], + PodName => $container->{PodName}, + State => $container->{State} + }; + } + + return $containers; +} + +sub list_pods { + my ($self, %options) = @_; + + my $results = $self->request( + endpoint => 'pods/json', + method => 'GET' + ); + + my $pods = {}; + foreach my $pod (@{$results}) { + $pods->{$pod->{Id}} = { + Name => $pod->{Name}, + Status => $pod->{Status} + }; + } + + return $pods; +} + +sub get_pod_infos { + my ($self, %options) = @_; + + my $inspect = $self->request( + endpoint => 'pods/' . $options{pod_name} . '/json', + method => 'GET' + ); + + my $stats = $self->request( + endpoint => 'pods/stats?namesOrIDs=' . $options{pod_name}, + method => 'GET' + ); + + my $pod = { + cpu => 0, + memory => 0, + running_containers => 0, + stopped_containers => 0, + paused_containers => 0, + state => @{$inspect}[0]->{State} + }; + + foreach my $container (@{$inspect->[0]->{Containers}}) { + $pod->{running_containers}++ if ($container->{State} eq 'running'); + $pod->{stopped_containers}++ if ($container->{State} eq 'exited'); + $pod->{paused_containers}++ if ($container->{State} eq 'paused'); + } + + foreach my $container (@{$stats}) { + my $cpu = $container->{CPU}; + if ($cpu =~ /^(\d+\.\d+)%/) { + $pod->{cpu} += $1; + } + my $memory = $container->{MemUsage}; + if ($memory =~ /^(\d+\.?\d*)([a-zA-Z]+)/) { + $memory = centreon::plugins::misc::convert_bytes(value => $1, unit => $2); + } + $pod->{memory} += $memory; + } + + return $pod; +} + +sub get_container_infos { + my ($self, %options) = @_; + + my $stats = $self->request( + endpoint => 'containers/stats?stream=false&containers=' . $options{container_name}, + method => 'GET' + ); + + my $containers = $self->list_containers(); + my $state; + foreach my $container_id (sort keys %{$containers}) { + if ($containers->{$container_id}->{Name} eq $options{container_name}) { + $state = $containers->{$container_id}->{State}; + } + } + + my $container = { + cpu_usage => $stats->{Stats}->[0]->{CPU}, + memory_usage => $stats->{Stats}->[0]->{MemUsage}, + io_read => $stats->{Stats}->[0]->{BlockInput}, + io_write => $stats->{Stats}->[0]->{BlockOutput}, + network_in => $stats->{Stats}->[0]->{NetInput}, + network_out => $stats->{Stats}->[0]->{NetOutput}, + state => $state + }; + + return $container; +} + +1; + +__END__ + +=head1 NAME + +Podman REST API. + +=head1 SYNOPSIS + +Podman Rest API custom mode. +To connect to the API with a socket, you must add the following command: +C<--curl-opt="CURLOPT_UNIX_SOCKET_PATH => 'PATH_TO_THE_SOCKET'"> +If you use a certificate, you must add the following commands: +C<--curl-opt="CURLOPT_CAINFO = 'PATH_TO_THE_CA_CERTIFICATE'"> +C<--curl-opt="CURLOPT_SSLCERT => 'PATH_TO_THE_CERTIFICATE'"> +C<--curl-opt="CURLOPT_SSLKEY => 'PATH_TO_THE_KEY'"> + +=head1 REST API OPTIONS + +=over 8 + +=item B<--hostname> + +Podman Rest API hostname. + +=item B<--port> + +Port used (Default: 443) + +=item B<--proto> + +Specify https if needed (Default: 'https') + +=item B<--url-path> + +Set path to get Podman Rest API information (Default: '/v5.0.0/libpod/') + +=item B<--timeout> + +Set timeout in seconds (Default: 30) + +=back + +=head1 DESCRIPTION + +B. + +=cut diff --git a/src/apps/podman/restapi/mode/containerusage.pm b/src/apps/podman/restapi/mode/containerusage.pm new file mode 100644 index 0000000000..c51657a8c8 --- /dev/null +++ b/src/apps/podman/restapi/mode/containerusage.pm @@ -0,0 +1,247 @@ +# +# Copyright 2025 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::podman::restapi::mode::containerusage; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; +use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng); + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'container', type => 0 } + ]; + + $self->{maps_counters}->{container} = [ + { label => 'cpu-usage', + nlabel => 'podman.container.cpu.usage.percent', + set => { + key_values => [ { name => 'cpu_usage' } ], + output_template => 'CPU: %.2f%%', + perfdatas => [ + { label => 'cpu', + value => 'cpu_usage', + template => '%.2f', + unit => '%', + min => 0, + max => 100 } + ] + } + }, + { label => 'memory-usage', + nlabel => 'podman.container.memory.usage.bytes', + set => { + key_values => [ { name => 'memory_usage' } ], + output_template => 'Memory: %s%s', + output_change_bytes => 1, + perfdatas => [ + { label => 'memory', + value => 'memory_usage', + template => '%s', + unit => 'B', + min => 0 } + ] + } + }, + { label => 'read-io', + nlabel => 'podman.container.io.read', + set => { + key_values => [ { name => 'io_read' } ], + output_template => 'Read : %s%s', + output_change_bytes => 1, + perfdatas => [ + { label => 'read.io', + value => 'io_read', + template => '%s', + unit => 'B', + min => 0 } + ] + } + }, + { label => 'write-io', + nlabel => 'podman.container.io.write', + set => { + key_values => [ { name => 'io_write' } ], + output_template => 'Write : %s%s', + output_change_bytes => 1, + perfdatas => [ + { label => 'write.io', + value => 'io_write', + template => '%s', + unit => 'B', + min => 0 } + ] + } + }, + { label => 'network-in', + nlabel => 'podman.container.network.in', + set => { + key_values => [ { name => 'network_in' } ], + output_template => 'Network in: %s%s', + output_change_bytes => 1, + perfdatas => [ + { label => 'network_in', + value => 'network_in', + template => '%s', + unit => 'B', + min => 0 } + ] + } + }, + { label => 'network-out', + nlabel => 'podman.container.network.out', + set => { + key_values => [ { name => 'network_out' } ], + output_template => 'Network out: %s%s', + output_change_bytes => 1, + perfdatas => [ + { label => 'network_out', + value => 'network_out', + template => '%s', + unit => 'B', + min => 0 } + ] + } + }, + { label => 'state', + type => 2, + warning_default => '%{state} =~ /Paused/', + critical_default => '%{state} =~ /Exited/', + set => { + key_values => [ { name => 'state' } ], + output_template => 'State: %s', + 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, force_new_perfdata => 1); + bless $self, $class; + + $options{options}->add_options(arguments => { + 'container-name:s' => { name => 'container_name' } + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + if (centreon::plugins::misc::is_empty($self->{option_results}->{container_name})) { + $self->{output}->add_option_msg(short_msg => "Need to specify --container-name option."); + $self->{output}->option_exit(); + } +} + +sub manage_selection { + my ($self, %options) = @_; + + my $container = $options{custom}->get_container_infos( + container_name => $self->{option_results}->{container_name} + ); + + $self->{container} = $container; +} + +1; + +__END__ + +=head1 MODE + +Check container usage. + +=over 8 + +=item B<--container-name> + +Container name. + +=item B<--warning-cpu-usage> + +Threshold warning for CPU usage. + +=item B<--critical-cpu-usage> + +Threshold critical for CPU usage. + +=item B<--warning-memory-usage> + +Threshold warning for memory usage. + +=item B<--critical-memory-usage> + +Threshold critical for memory usage. + +=item B<--warning-read-io> + +Threshold warning for read IO. + +=item B<--critical-read-io> + +Threshold critical for read IO. + +=item B<--warning-write-io> + +Threshold warning for write IO. + +=item B<--critical-write-io> + +Threshold critical for write IO. + +=item B<--warning-network-in> + +Threshold warning for network in. + +=item B<--critical-network-in> + +Threshold critical for network in. + +=item B<--warning-network-out> + +Threshold warning for network out. + +=item B<--critical-network-out> + +Threshold critical for network out. + +=item B<--warning-container-state> + +Define the conditions to match for the state to be WARNING (default: C<'%{state} =~ /Paused/'>). +You can use the following variables: C<%{state}> + +=item B<--critical-container-state> + +Define the conditions to match for the state to be CRITICAL (default: C<'%{state} =~ /Exited/'>). +You can use the following variables: C<%{state}> + +=back + +=cut diff --git a/src/apps/podman/restapi/mode/listcontainers.pm b/src/apps/podman/restapi/mode/listcontainers.pm new file mode 100644 index 0000000000..69a355830d --- /dev/null +++ b/src/apps/podman/restapi/mode/listcontainers.pm @@ -0,0 +1,94 @@ +# +# Copyright 2025 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::podman::restapi::mode::listcontainers; + +use base qw(centreon::plugins::mode); + +use strict; +use warnings; + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $options{options}->add_options(arguments => + { + }); + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::init(%options); +} + +sub run { + my ($self, %options) = @_; + + my $containers = $options{custom}->list_containers(); + foreach my $container_id (sort keys %{$containers}) { + $self->{output}->output_add(long_msg => '[id = ' . $container_id . "]" . + " [name = '" . $containers->{$container_id}->{Name} . "']" . + " [pod = '" . $containers->{$container_id}->{PodName} . "']" . + " [state = '" . $containers->{$container_id}->{State} . "']" + ); + } + + $self->{output}->output_add(severity => 'OK', + short_msg => 'Containers:'); + $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 => [ 'id', 'name', 'pod', 'state' ]); +} + +sub disco_show { + my ($self, %options) = @_; + + my $containers = $options{custom}->list_containers(); + foreach my $container_id (sort keys %{$containers}) { + $self->{output}->add_disco_entry(name => $containers->{$container_id}->{Name}, + pod => $containers->{$container_id}->{PodName}, + state => $containers->{$container_id}->{State}, + id => $container_id, + ); + } +} + +1; + +__END__ + +=head1 MODE + +List containers. + +=over 8 + +=back + +=cut + diff --git a/src/apps/podman/restapi/mode/listpods.pm b/src/apps/podman/restapi/mode/listpods.pm new file mode 100644 index 0000000000..517e734e3e --- /dev/null +++ b/src/apps/podman/restapi/mode/listpods.pm @@ -0,0 +1,92 @@ +# +# Copyright 2025 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::podman::restapi::mode::listpods; + +use base qw(centreon::plugins::mode); + +use strict; +use warnings; + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $options{options}->add_options(arguments => + { + }); + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::init(%options); +} + +sub run { + my ($self, %options) = @_; + + my $pods = $options{custom}->list_pods(); + foreach my $pod_id (sort keys %{$pods}) { + $self->{output}->output_add(long_msg => '[id = ' . $pod_id . "]" . + " [name = '" . $pods->{$pod_id}->{Name} . "']" . + " [status = '" . $pods->{$pod_id}->{Status} . "']" + ); + } + + $self->{output}->output_add(severity => 'OK', + short_msg => 'Pods:'); + $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 => [ 'id', 'name', 'status' ]); +} + +sub disco_show { + my ($self, %options) = @_; + + my $pods = $options{custom}->list_pods(); + foreach my $pod_id (sort keys %{$pods}) { + $self->{output}->add_disco_entry(name => $pods->{$pod_id}->{Name}, + state => $pods->{$pod_id}->{Status}, + id => $pod_id, + ); + } +} + +1; + +__END__ + +=head1 MODE + +List pods. + +=over 8 + +=back + +=cut + diff --git a/src/apps/podman/restapi/mode/podstatus.pm b/src/apps/podman/restapi/mode/podstatus.pm new file mode 100644 index 0000000000..1dd4939107 --- /dev/null +++ b/src/apps/podman/restapi/mode/podstatus.pm @@ -0,0 +1,224 @@ +# +# Copyright 2025 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::podman::restapi::mode::podstatus; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; +use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng); + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'pod', type => 0 } + ]; + + $self->{maps_counters}->{pod} = [ + { label => 'cpu-usage', + nlabel => 'podman.pod.cpu.usage.percent', + set => { + key_values => [ { name => 'cpu' } ], + output_template => 'CPU: %.2f%%', + perfdatas => [ + { label => 'cpu', + value => 'cpu', + template => '%.2f', + unit => '%', + min => 0, + max => 100 } + ] + } + }, + { label => 'memory-usage', + nlabel => 'podman.pod.memory.usage.bytes', set => { + key_values => [ { name => 'memory' } ], + output_template => 'Memory: %s%s', + output_change_bytes => 1, + perfdatas => [ + { label => 'memory', + value => 'memory', + template => '%s', + unit => 'B', + min => 0 } + ] + } + }, + { label => 'running-containers', + nlabel => 'podman.pod.containers.running.count', + set => { + key_values => [ { name => 'containers_running' } ], + output_template => 'Running containers: %s', + perfdatas => [ + { label => 'containers_running', + value => 'containers_running', + template => '%s', + min => 0 } + ] + } + }, + { label => 'stopped-containers', + nlabel => 'podman.pod.containers.stopped.count', + set => { + key_values => [ { name => 'containers_stopped' } ], + output_template => 'Stopped containers: %s', + perfdatas => [ + { label => 'containers_stopped', + value => 'containers_stopped', + template => '%s', + min => 0 } + ] + } + }, + { label => 'paused-containers', + nlabel => 'podman.pod.containers.paused.count', + set => { + key_values => [ { name => 'containers_paused' } ], + output_template => 'Paused containers: %s', + perfdatas => [ + { label => 'containers_paused', + value => 'containers_paused', + template => '%s', + min => 0 } + ] + } + }, + { label => 'state', + type => 2, + warning_default => '%{state} =~ /Exited/', + critical_default => '%{state} =~ /Degraded/', + set => { + key_values => [ { name => 'state' } ], + output_template => 'State: %s', + 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, force_new_perfdata => 1); + bless $self, $class; + + $options{options}->add_options(arguments => { + 'pod-name:s' => { name => 'pod_name' } + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + if (centreon::plugins::misc::is_empty($self->{option_results}->{pod_name})) { + $self->{output}->add_option_msg(short_msg => "Need to specify --pod-name option."); + $self->{output}->option_exit(); + } +} + +sub manage_selection { + my ($self, %options) = @_; + + my $result = $options{custom}->get_pod_infos( + pod_name => $self->{option_results}->{pod_name} + ); + + $self->{pod} = { + cpu => $result->{cpu}, + memory => $result->{memory}, + containers_running => $result->{running_containers}, + containers_stopped => $result->{stopped_containers}, + containers_paused => $result->{paused_containers}, + state => $result->{state} + }; +} + +1; + +__END__ + +=head1 MODE + +Check node status. + +=over 8 + +=item B<--pod-name> + +Pod name. + +=item B<--warning-cpu-usage> + +Threshold warning for CPU usage. + +=item B<--critical-cpu-usage> + +Threshold critical for CPU usage. + +=item B<--warning-memory-usage> + +Threshold warning for memory usage. + +=item B<--critical-memory-usage> + +Threshold critical for memory usage. + +=item B<--warning-running-containers> + +Threshold warning for running containers. + +=item B<--critical-running-containers> + +Threshold critical for running containers. + +=item B<--warning-stopped-containers> + +Threshold warning for stopped containers. + +=item B<--critical-stopped-containers> + +Threshold critical for stopped containers. + +=item B<--warning-paused-containers> + +Threshold warning for paused containers. + +=item B<--critical-paused-containers> + +Threshold critical for paused containers. + +=item B<--warning-state> + +Define the conditions to match for the state to be WARNING (default: C<'%{state} =~ /Exited/'>). +You can use the following variables: C<%{state}> + +=item B<--critical-state> + +Define the conditions to match for the state to be CRITICAL (default: C<'%{state} =~ /Degraded/'>). +You can use the following variables: C<%{state}> + +=back + +=cut diff --git a/src/apps/podman/restapi/mode/systemstatus.pm b/src/apps/podman/restapi/mode/systemstatus.pm new file mode 100644 index 0000000000..a4731c2cf5 --- /dev/null +++ b/src/apps/podman/restapi/mode/systemstatus.pm @@ -0,0 +1,240 @@ +# +# Copyright 2025 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::podman::restapi::mode::systemstatus; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; +use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold); + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'system', type => 0 } + ]; + + $self->{maps_counters}->{system} = [ + { label => 'cpu-usage', + nlabel => 'podman.system.cpu.usage.percent', + set => { + key_values => [ + { name => 'cpu_usage' } + ], + output_template => 'CPU: %.2f%%', + perfdatas => [ + { label => 'cpu', + template => '%.2f', + min => 0, + max => 100, + unit => '%' } + ] + } + }, + { label => 'memory-usage', + nlabel => 'podman.system.memory.usage.bytes', + set => { + key_values => [ + { name => 'memory_usage' } + ], + output_template => 'Memory: %s%s', + output_change_bytes => 1, + perfdatas => [ + { label => 'memory', + template => '%s', + min => 0, + unit => 'B' } + ] + } + }, + { label => 'swap-usage', + nlabel => 'podman.system.swap.usage.bytes', + set => { + key_values => [ + { name => 'swap_usage' } + ], + output_template => 'Swap: %s%s', + output_change_bytes => 1, + perfdatas => [ + { label => 'swap', + template => '%s', + min => 0, + unit => 'B' } + ] + } + }, + { label => 'containers-running', + nlabel => 'podman.system.containers.running.count', + set => { + key_values => [ + { name => 'running_containers' }, + { name => 'total_containers' } + ], + output_template => 'Running containers: %s', + perfdatas => [ + { label => 'running_containers', + template => '%s', + min => 0, + max => 'total_containers', + unit => '' } + ] + } + }, + { label => 'containers-stopped', + nlabel => 'podman.system.containers.stopped.count', + set => { + key_values => [ + { name => 'stopped_containers' }, + { name => 'total_containers' } + ], + output_template => 'Stopped containers: %s', + perfdatas => [ + { label => 'stopped_containers', + template => '%s', + min => 0, + max => 'total_containers', + unit => '' } + ] + } + }, + { label => 'uptime', + nlabel => 'podman.system.uptime.seconds', + set => { + key_values => [ + { name => 'uptime' } + ], + output_template => 'Uptime: %s s', + perfdatas => [ + { label => 'uptime', + template => '%s', + min => 0, + unit => 's' } + ] + } + } + ]; +} + +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 => {}); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); +} + +sub manage_selection { + my ($self, %options) = @_; + + my $results = $options{custom}->system_info(); + + my $uptime_string = $results->{host}->{uptime}; + my $uptime = 0; + if ($uptime_string =~ /(\d+)h/) { + $uptime += $1 * 3600; + } + if ($uptime_string =~ /(\d+)m/) { + $uptime += $1 * 60; + } + if ($uptime_string =~ /(\d+)s/) { + $uptime += $1; + } + + $self->{system} = { + cpu_usage => $results->{host}->{cpuUtilization}->{userPercent} + $results->{host}->{cpuUtilization}->{systemPercent}, + memory_usage => $results->{host}->{memTotal} - $results->{host}->{memFree}, + swap_usage => $results->{host}->{swapTotal} - $results->{host}->{swapFree}, + running_containers => $results->{store}->{containerStore}->{running}, + stopped_containers => $results->{store}->{containerStore}->{stopped}, + total_containers => $results->{store}->{containerStore}->{number}, + uptime => $uptime + }; +} + +1; + +__END__ + +=head1 MODE + +Check Podman system status. + +=over 8 + +=item B<--warning-cpu-usage> + +Threshold warning in percent for CPU usage. + +=item B<--critical-cpu-usage> + +Threshold critical in percent for CPU usage. + +=item B<--warning-memory-usage> + +Threshold warning in bytes for memory usage. + +=item B<--critical-memory-usage> + +Threshold critical in bytes for memory usage. + +=item B<--warning-swap-usage> + +Threshold warning in bytes for swap usage. + +=item B<--critical-swap-usage> + +Threshold critical in bytes for swap usage. + +=item B<--warning-containers-running> + +Threshold warning for the number of running containers. + +=item B<--critical-containers-running> + +Threshold critical for the number of running containers. + +=item B<--warning-containers-stopped> + +Threshold warning for the number of stopped containers. + +=item B<--critical-containers-stopped> + +Threshold critical for the number of stopped containers. + +=item B<--warning-uptime> + +Threshold warning for uptime in seconds. + +=item B<--critical-uptime> + +Threshold critical for uptime in seconds. + +=back + +=cut diff --git a/src/apps/podman/restapi/plugin.pm b/src/apps/podman/restapi/plugin.pm new file mode 100644 index 0000000000..91afd62e89 --- /dev/null +++ b/src/apps/podman/restapi/plugin.pm @@ -0,0 +1,53 @@ +# +# Copyright 2024 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::podman::restapi::plugin; + +use strict; +use warnings; +use base qw(centreon::plugins::script_custom); + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{version} = '1.0'; + $self->{modes} = { + 'container-usage' => 'apps::podman::restapi::mode::containerusage', + 'list-containers' => 'apps::podman::restapi::mode::listcontainers', + 'list-pods' => 'apps::podman::restapi::mode::listpods', + 'pod-status' => 'apps::podman::restapi::mode::podstatus', + 'system-status' => 'apps::podman::restapi::mode::systemstatus' + }; + + $self->{custom_modes}->{api} = 'apps::podman::restapi::custom::api'; + return $self; +} + +1; + +__END__ + +=head1 PLUGIN DESCRIPTION + +Check Podman and containers through its HTTPS Rest API (https://docs.podman.io/en/latest/_static/api.html). + +=cut diff --git a/tests/apps/podman/restapi/containerusage.robot b/tests/apps/podman/restapi/containerusage.robot new file mode 100644 index 0000000000..252756525b --- /dev/null +++ b/tests/apps/podman/restapi/containerusage.robot @@ -0,0 +1,62 @@ +*** Settings *** +Documentation Test the Podman container-usage mode + +Resource ${CURDIR}${/}..${/}..${/}..${/}resources/import.resource + +Suite Setup Start Mockoon ${MOCKOON_JSON} +Suite Teardown Stop Mockoon +Test Timeout 120s + +*** Variables *** +${MOCKOON_JSON} ${CURDIR}${/}podman.json + +${cmd} ${CENTREON_PLUGINS} +... --plugin=apps::podman::restapi::plugin +... --custommode=api +... --mode=container-usage +... --hostname=${HOSTNAME} +... --port=${APIPORT} +... --proto=http + + +*** Test Cases *** +Container usage ${tc} + [Documentation] Check the container usage + [Tags] apps podman restapi + + ${command} Catenate + ... ${cmd} + ... --container-name=wordpress + ... ${extraoptions} + + Ctn Run Command And Check Result As Strings ${command} ${expected_result} + + Examples: tc extraoptions expected_result -- + ... 1 ${EMPTY} OK: CPU: 0.11%, Memory: 10.85MB, Read : 435.65MB, Write : 941.43MB, Network in: 1006.00B, Network out: 2.10KB, State: running | 'podman.container.cpu.usage.percent'=0.11%;;;0;100 'podman.container.memory.usage.bytes'=11374592B;;;0; 'podman.container.io.read'=456812354B;;;0; 'podman.container.io.write'=987156423B;;;0; 'podman.container.network.in'=1006B;;;0; 'podman.container.network.out'=2146B;;;0; + ... 2 --warning-cpu-usage=0.1 WARNING: CPU: 0.11% | 'podman.container.cpu.usage.percent'=0.11%;0:0.1;;0;100 'podman.container.memory.usage.bytes'=11374592B;;;0; 'podman.container.io.read'=456812354B;;;0; 'podman.container.io.write'=987156423B;;;0; 'podman.container.network.in'=1006B;;;0; 'podman.container.network.out'=2146B;;;0; + ... 3 --critical-cpu-usage=0.1 CRITICAL: CPU: 0.11% | 'podman.container.cpu.usage.percent'=0.11%;;0:0.1;0;100 'podman.container.memory.usage.bytes'=11374592B;;;0; 'podman.container.io.read'=456812354B;;;0; 'podman.container.io.write'=987156423B;;;0; 'podman.container.network.in'=1006B;;;0; 'podman.container.network.out'=2146B;;;0; + ... 4 --warning-memory-usage=10000000 WARNING: Memory: 10.85MB | 'podman.container.cpu.usage.percent'=0.11%;;;0;100 'podman.container.memory.usage.bytes'=11374592B;0:10000000;;0; 'podman.container.io.read'=456812354B;;;0; 'podman.container.io.write'=987156423B;;;0; 'podman.container.network.in'=1006B;;;0; 'podman.container.network.out'=2146B;;;0; + ... 5 --critical-memory-usage=10000000 CRITICAL: Memory: 10.85MB | 'podman.container.cpu.usage.percent'=0.11%;;;0;100 'podman.container.memory.usage.bytes'=11374592B;;0:10000000;0; 'podman.container.io.read'=456812354B;;;0; 'podman.container.io.write'=987156423B;;;0; 'podman.container.network.in'=1006B;;;0; 'podman.container.network.out'=2146B;;;0; + ... 6 --warning-read-io=200000000 WARNING: Read : 435.65MB | 'podman.container.cpu.usage.percent'=0.11%;;;0;100 'podman.container.memory.usage.bytes'=11374592B;;;0; 'podman.container.io.read'=456812354B;0:200000000;;0; 'podman.container.io.write'=987156423B;;;0; 'podman.container.network.in'=1006B;;;0; 'podman.container.network.out'=2146B;;;0; + ... 7 --critical-read-io=400000000 CRITICAL: Read : 435.65MB | 'podman.container.cpu.usage.percent'=0.11%;;;0;100 'podman.container.memory.usage.bytes'=11374592B;;;0; 'podman.container.io.read'=456812354B;;0:400000000;0; 'podman.container.io.write'=987156423B;;;0; 'podman.container.network.in'=1006B;;;0; 'podman.container.network.out'=2146B;;;0; + ... 8 --warning-write-io=500000000 WARNING: Write : 941.43MB | 'podman.container.cpu.usage.percent'=0.11%;;;0;100 'podman.container.memory.usage.bytes'=11374592B;;;0; 'podman.container.io.read'=456812354B;;;0; 'podman.container.io.write'=987156423B;0:500000000;;0; 'podman.container.network.in'=1006B;;;0; 'podman.container.network.out'=2146B;;;0; + ... 9 --critical-write-io=750000000 CRITICAL: Write : 941.43MB | 'podman.container.cpu.usage.percent'=0.11%;;;0;100 'podman.container.memory.usage.bytes'=11374592B;;;0; 'podman.container.io.read'=456812354B;;;0; 'podman.container.io.write'=987156423B;;0:750000000;0; 'podman.container.network.in'=1006B;;;0; 'podman.container.network.out'=2146B;;;0; + +Container usage ${tc} + [Documentation] Check the container usage + [Tags] apps podman restapi + + ${command} Catenate + ... ${cmd} + ... --container-name=wordpress + ... ${extraoptions} + + Ctn Run Command And Check Result As Strings ${command} ${expected_result} + + Examples: tc extraoptions expected_result -- + ... 10 --warning-network-in=500 WARNING: Network in: 1006.00B | 'podman.container.cpu.usage.percent'=0.11%;;;0;100 'podman.container.memory.usage.bytes'=11374592B;;;0; 'podman.container.io.read'=456812354B;;;0; 'podman.container.io.write'=987156423B;;;0; 'podman.container.network.in'=1006B;0:500;;0; 'podman.container.network.out'=2146B;;;0; + ... 11 --critical-network-in=1000 CRITICAL: Network in: 1006.00B | 'podman.container.cpu.usage.percent'=0.11%;;;0;100 'podman.container.memory.usage.bytes'=11374592B;;;0; 'podman.container.io.read'=456812354B;;;0; 'podman.container.io.write'=987156423B;;;0; 'podman.container.network.in'=1006B;;0:1000;0; 'podman.container.network.out'=2146B;;;0; + ... 12 --warning-network-out=1000 WARNING: Network out: 2.10KB | 'podman.container.cpu.usage.percent'=0.11%;;;0;100 'podman.container.memory.usage.bytes'=11374592B;;;0; 'podman.container.io.read'=456812354B;;;0; 'podman.container.io.write'=987156423B;;;0; 'podman.container.network.in'=1006B;;;0; 'podman.container.network.out'=2146B;0:1000;;0; + ... 13 --critical-network-out=2000 CRITICAL: Network out: 2.10KB | 'podman.container.cpu.usage.percent'=0.11%;;;0;100 'podman.container.memory.usage.bytes'=11374592B;;;0; 'podman.container.io.read'=456812354B;;;0; 'podman.container.io.write'=987156423B;;;0; 'podman.container.network.in'=1006B;;;0; 'podman.container.network.out'=2146B;;0:2000;0; + ... 14 --warning-state='\\\%{state} =~ /running/' WARNING: State: running | 'podman.container.cpu.usage.percent'=0.11%;;;0;100 'podman.container.memory.usage.bytes'=11374592B;;;0; 'podman.container.io.read'=456812354B;;;0; 'podman.container.io.write'=987156423B;;;0; 'podman.container.network.in'=1006B;;;0; 'podman.container.network.out'=2146B;;;0; + ... 15 --critical-state='\\\%{state} =~ /running/' CRITICAL: State: running | 'podman.container.cpu.usage.percent'=0.11%;;;0;100 'podman.container.memory.usage.bytes'=11374592B;;;0; 'podman.container.io.read'=456812354B;;;0; 'podman.container.io.write'=987156423B;;;0; 'podman.container.network.in'=1006B;;;0; 'podman.container.network.out'=2146B;;;0; diff --git a/tests/apps/podman/restapi/listcontainers.robot b/tests/apps/podman/restapi/listcontainers.robot new file mode 100644 index 0000000000..9226940cfa --- /dev/null +++ b/tests/apps/podman/restapi/listcontainers.robot @@ -0,0 +1,36 @@ +*** Settings *** +Documentation Test the Podman list-containers mode + +Resource ${CURDIR}${/}..${/}..${/}..${/}resources/import.resource + +Suite Setup Start Mockoon ${MOCKOON_JSON} +Suite Teardown Stop Mockoon +Test Timeout 120s + + +*** Variables *** +${MOCKOON_JSON} ${CURDIR}${/}podman.json + +${cmd} ${CENTREON_PLUGINS} +... --plugin=apps::podman::restapi::plugin +... --custommode=api +... --mode=list-containers +... --hostname=${HOSTNAME} +... --port=${APIPORT} +... --proto=http + +*** Test Cases *** +List-Containers ${tc} + [Documentation] Check list-containers results + [Tags] apps podman restapi + + ${command} Catenate + ... ${cmd} + ... ${extraoptions} + + Ctn Run Command And Check Result As Regexp ${command} ${expected_result} + + Examples: tc extraoptions expected_result -- + ... 1 ${EMPTY} ^Containers: (\\\\n\\\\[.*\\\\]){3}\\\\Z + ... 2 --disco-show \\\\<\\\\?xml version="1.0" encoding="utf-8"\\\\?\\\\>\\\\n\\\\(\\\\n\\\\s*\\\\