diff --git a/cloud/vmware/velocloud/restapi/custom/api.pm b/cloud/vmware/velocloud/restapi/custom/api.pm index 87c1e1a7c..2c38cc1ec 100644 --- a/cloud/vmware/velocloud/restapi/custom/api.pm +++ b/cloud/vmware/velocloud/restapi/custom/api.pm @@ -167,7 +167,7 @@ sub get_session_cookie { $session = $1 if ($cookie =~ /^velocloud\.session=(.+?);/); } - if (!defined($session)) { + if (!defined($session) || $session eq '') { $self->{output}->add_option_msg(short_msg => "Cannot get session cookie: " . $message); $self->{output}->option_exit(); } @@ -177,6 +177,12 @@ sub get_session_cookie { sub get_entreprise_id { my ($self, %options) = @_; + + if (!defined($self->{session_cookie})) { + $self->get_session_cookie(); + } + + $self->settings(); my $content = $self->{http}->request( method => 'POST', @@ -197,6 +203,12 @@ sub get_entreprise_id { sub request_api { my ($self, %options) = @_; + + if (!defined($self->{session_cookie})) { + $self->get_session_cookie(); + } + + $self->settings(); my $encoded_form_post; eval { @@ -225,6 +237,11 @@ sub request_api { $self->{output}->add_option_msg(short_msg => "Cannot decode json response"); $self->{output}->option_exit(); } + if (ref($decoded) ne 'ARRAY' && defined($decoded->{error})) { + $self->{output}->add_option_msg(short_msg => "API returned error code '" . $decoded->{error}->{code} . + "', message '" . $decoded->{error}->{message} . "'"); + $self->{output}->option_exit(); + } return $decoded; } @@ -232,12 +249,6 @@ sub request_api { sub list_edges { my ($self, %options) = @_; - if (!defined($self->{session_cookie})) { - $self->get_session_cookie(); - } - - $self->settings(); - if (!defined($self->{entreprise_id})) { $self->get_entreprise_id(); } @@ -251,6 +262,18 @@ sub list_edges { return $response; } +sub list_links { + my ($self, %options) = @_; + + my $response = $self->request_api( + method => 'POST', + url_path => $self->{api_path} . '/metrics/getEdgeLinkMetrics', + query_form_post => { id => $options{edge_id} } + ); + + return $response; +} + sub DESTROY { my $self = shift; diff --git a/cloud/vmware/velocloud/restapi/mode/discovery.pm b/cloud/vmware/velocloud/restapi/mode/discovery.pm new file mode 100644 index 000000000..1a37fc48b --- /dev/null +++ b/cloud/vmware/velocloud/restapi/mode/discovery.pm @@ -0,0 +1,107 @@ +# +# Copyright 2019 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 cloud::vmware::velocloud::restapi::mode::discovery; + +use base qw(centreon::plugins::mode); + +use strict; +use warnings; +use JSON::XS; + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $options{options}->add_options(arguments => { + "prettify" => { name => 'prettify' }, + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::init(%options); +} + +sub run { + my ($self, %options) = @_; + + my @disco_data; + my $disco_stats; + + $disco_stats->{start_time} = time(); + + my $results = $options{custom}->list_edges; + + foreach my $edge (@{$results}) { + my %edge; + $edge{id} = $edge->{id}; + $edge{site_id} = $edge->{siteId}; + $edge{enterprise_id} = $edge->{enterpriseId}; + $edge{activation_state} = $edge->{activationState}; + $edge{model_number} = $edge->{modelNumber}; + $edge{device_family} = $edge->{deviceFamily}; + $edge{name} = $edge->{name}; + $edge{description} = $edge->{description}; + $edge{edge_state} = $edge->{edgeState}; + $edge{service_state} = $edge->{serviceState}; + $edge{ha_state} = $edge->{haState}; + + push @disco_data, \%edge; + } + + $disco_stats->{end_time} = time(); + $disco_stats->{duration} = $disco_stats->{end_time} - $disco_stats->{start_time}; + $disco_stats->{discovered_items} = @disco_data; + $disco_stats->{results} = \@disco_data; + + my $encoded_data; + eval { + if (defined($self->{option_results}->{prettify})) { + $encoded_data = JSON::XS->new->utf8->pretty->encode($disco_stats); + } else { + $encoded_data = JSON::XS->new->utf8->encode($disco_stats); + } + }; + if ($@) { + $encoded_data = '{"code":"encode_error","message":"Cannot encode discovered data into JSON format"}'; + } + + $self->{output}->output_add(short_msg => $encoded_data); + $self->{output}->display(nolabel => 1, force_ignore_perfdata => 1); + $self->{output}->exit(); +} + +1; + +__END__ + +=head1 MODE + +Resources discovery. + +=over 8 + +=back + +=cut diff --git a/cloud/vmware/velocloud/restapi/mode/edgestatus.pm b/cloud/vmware/velocloud/restapi/mode/edgestatus.pm new file mode 100644 index 000000000..59567e7d1 --- /dev/null +++ b/cloud/vmware/velocloud/restapi/mode/edgestatus.pm @@ -0,0 +1,165 @@ +# +# Copyright 2019 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 cloud::vmware::velocloud::restapi::mode::edgestatus; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; +use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold); + +sub custom_status_output { + my ($self, %options) = @_; + + return sprintf("State is '%s', Service State is '%s', HA State is '%s', Activation State is '%s'", + $self->{result_values}->{edge_state}, + $self->{result_values}->{service_state}, + $self->{result_values}->{ha_state}, + $self->{result_values}->{activation_state}); +} + +sub custom_status_calc { + my ($self, %options) = @_; + + $self->{result_values}->{edge_state} = $options{new_datas}->{$self->{instance} . '_edge_state'}; + $self->{result_values}->{service_state} = $options{new_datas}->{$self->{instance} . '_service_state'}; + $self->{result_values}->{ha_state} = $options{new_datas}->{$self->{instance} . '_ha_state'}; + $self->{result_values}->{activation_state} = $options{new_datas}->{$self->{instance} . '_activation_state'}; + + return 0; +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'edges', type => 1, cb_prefix_output => 'prefix_output', + message_multiple => 'All edges status are ok' }, + ]; + + $self->{maps_counters}->{edges} = [ + { label => 'status', set => { + key_values => [ { name => 'edge_state' }, { name => 'service_state' }, { name => 'ha_state' }, + { name => 'activation_state' }, { name => 'display' } ], + closure_custom_calc => $self->can('custom_status_calc'), + closure_custom_output => $self->can('custom_status_output'), + closure_custom_perfdata => sub { return 0; }, + closure_custom_threshold_check => \&catalog_status_threshold, + } + }, + ]; +} + +sub prefix_output { + my ($self, %options) = @_; + + return "Edge '" . $options{instance_value}->{display} . "' "; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $options{options}->add_options(arguments => { + "filter-name:s" => { name => 'filter_name' }, + "filter-id:s" => { name => 'filter_id' }, + "warning-status:s" => { name => 'warning_status', default => '' }, + "critical-status:s" => { name => 'critical_status', default => '' }, + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + $self->change_macros(macros => ['warning_status', 'critical_status']); +} + +sub manage_selection { + my ($self, %options) = @_; + + $self->{edges} = {}; + + my $results = $options{custom}->list_edges; + + foreach my $edge (@{$results}) { + if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' && + $edge->{name} !~ /$self->{option_results}->{filter_name}/) { + $self->{output}->output_add(long_msg => "skipping '" . $edge->{name} . "'.", debug => 1); + next; + } + if (defined($self->{option_results}->{filter_id}) && $self->{option_results}->{filter_id} ne '' && + $edge->{id} !~ /$self->{option_results}->{filter_id}/) { + $self->{output}->output_add(long_msg => "skipping '" . $edge->{id} . "'.", debug => 1); + next; + } + + $self->{edges}->{$edge->{id}} = { + display => $edge->{name}, + edge_state => $edge->{edgeState}, + service_state => $edge->{serviceState}, + ha_state => $edge->{haState}, + activation_state => $edge->{activationState} + } + } + + if (scalar(keys %{$self->{edges}}) <= 0) { + $self->{output}->add_option_msg(short_msg => "No edge found."); + $self->{output}->option_exit(); + } +} + +1; + +__END__ + +=head1 MODE + +Check edge status. + +=over 8 + +=item B<--filter-name> + +Filter edge by name (Can be a regexp). + +=item B<--filter-id> + +Filter edge by id (Can be a regexp). + +=item B<--warning-status> + +Set warning threshold for status (Default: ''). +Can used special variables like: %{edge_state}, %{service_state}, +%{ha_state}, %{activation_state}. + +=item B<--critical-status> + +Set critical threshold for status (Default: ''). +Can used special variables like: %{edge_state}, %{service_state}, +%{ha_state}, %{activation_state}. + +=back + +=cut diff --git a/cloud/vmware/velocloud/restapi/mode/linkstatus.pm b/cloud/vmware/velocloud/restapi/mode/linkstatus.pm new file mode 100644 index 000000000..64468786d --- /dev/null +++ b/cloud/vmware/velocloud/restapi/mode/linkstatus.pm @@ -0,0 +1,291 @@ +# +# Copyright 2019 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 cloud::vmware::velocloud::restapi::mode::linkstatus; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; +use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold); +use Digest::MD5 qw(md5_hex); + +sub custom_status_output { + my ($self, %options) = @_; + + return sprintf("Status is '%s', VPN State is '%s'", + $self->{result_values}->{state}, $self->{result_values}->{vpn_state}); +} + +sub custom_status_calc { + my ($self, %options) = @_; + + $self->{result_values}->{interface} = $options{new_datas}->{$self->{instance} . '_interface'}; + $self->{result_values}->{state} = $options{new_datas}->{$self->{instance} . '_state'}; + $self->{result_values}->{vpn_state} = $options{new_datas}->{$self->{instance} . '_vpn_state'}; + $self->{result_values}->{display} = $options{new_datas}->{$self->{instance} . '_display'}; + + return 0; +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'edges', type => 3, cb_prefix_output => 'prefix_edge_output', cb_long_output => 'long_output', + message_multiple => 'All edges links are ok', indent_long_output => ' ', + group => [ + { name => 'links', display_long => 1, cb_prefix_output => 'prefix_link_output', + message_multiple => 'All links status are ok', type => 1 }, + ] + } + ]; + + $self->{maps_counters}->{links} = [ + { label => 'status', set => { + key_values => [ { name => 'interface' }, { name => 'state' }, { name => 'vpn_state' }, + { name => 'display' }, { name => 'id' } ], + closure_custom_calc => $self->can('custom_status_calc'), + closure_custom_output => $self->can('custom_status_output'), + closure_custom_perfdata => sub { return 0; }, + closure_custom_threshold_check => \&catalog_status_threshold, + } + }, + { label => 'traffic-in', nlabel => 'link.traffic.in.bitspersecond', set => { + key_values => [ { name => 'traffic_in' }, { name => 'id' } ], + output_template => 'Traffic In: %s %s/s', + perfdatas => [ + { value => 'traffic_in_absolute', template => '%.2f', + min => 0, unit => 'b/s', label_extra_instance => 1, instance_use => 'id_absolute' }, + ], + } + }, + { label => 'traffic-out', nlabel => 'link.traffic.out.bitspersecond', set => { + key_values => [ { name => 'traffic_out' }, { name => 'id' } ], + output_template => 'Traffic Out: %s %s/s', + perfdatas => [ + { value => 'traffic_out_absolute', template => '%.2f', + min => 0, unit => 'b/s', label_extra_instance => 1, instance_use => 'id_absolute' }, + ], + } + }, + { label => 'latency-in', nlabel => 'link.latency.in.milliseconds', set => { + key_values => [ { name => 'latency_in' }, { name => 'id' } ], + output_template => 'Latency In: %.2f ms', + perfdatas => [ + { value => 'latency_in_absolute', template => '%.2f', + min => 0, unit => 'ms', label_extra_instance => 1, instance_use => 'id_absolute' }, + ], + } + }, + { label => 'latency-out', nlabel => 'link.latency.out.milliseconds', set => { + key_values => [ { name => 'latency_out' }, { name => 'id' } ], + output_template => 'Latency Out: %.2f ms', + perfdatas => [ + { value => 'latency_out_absolute', template => '%.2f', + min => 0, unit => 'ms', label_extra_instance => 1, instance_use => 'id_absolute' }, + ], + } + }, + { label => 'jitter-in', nlabel => 'link.jitter.in.milliseconds', set => { + key_values => [ { name => 'jitter_in' }, { name => 'id' } ], + output_template => 'Jitter In: %.2f ms', + perfdatas => [ + { value => 'jitter_in_absolute', template => '%.2f', + min => 0, unit => 'ms', label_extra_instance => 1, instance_use => 'id_absolute' }, + ], + } + }, + { label => 'jitter-out', nlabel => 'link.jitter.out.milliseconds', set => { + key_values => [ { name => 'jitter_out' }, { name => 'id' } ], + output_template => 'Jitter Out: %.2f ms', + perfdatas => [ + { value => 'jitter_out_absolute', template => '%.2f', + min => 0, unit => 'ms', label_extra_instance => 1, instance_use => 'id_absolute' }, + ], + } + }, + { label => 'packet-loss-in', nlabel => 'link.packet.loss.in.percentage', set => { + key_values => [ { name => 'packet_loss_in' }, { name => 'id' } ], + output_template => 'Packet Loss In: %.2f%%', + perfdatas => [ + { value => 'packet_loss_in_absolute', template => '%.2f', + min => 0, unit => '%', label_extra_instance => 1, instance_use => 'id_absolute' }, + ], + } + }, + { label => 'packet-loss-out', nlabel => 'link.packet.loss.out.percentage', set => { + key_values => [ { name => 'packet_loss_out' }, { name => 'id' } ], + output_template => 'Packet Loss Out: %.2f%%', + perfdatas => [ + { value => 'packet_loss_out_absolute', template => '%.2f', + min => 0, unit => '%', label_extra_instance => 1, instance_use => 'id_absolute' }, + ], + } + }, + ]; +} + +sub prefix_edge_output { + my ($self, %options) = @_; + + return "Edge '" . $options{instance_value}->{display} . "' "; +} + +sub prefix_link_output { + my ($self, %options) = @_; + + return "Link '" . $options{instance_value}->{display} . "' [Id: " . $options{instance_value}->{id} . "] "; +} + +sub long_output { + my ($self, %options) = @_; + + return "Checking edge '" . $options{instance_value}->{display} . "' [Id: " . $options{instance_value}->{id} . "] "; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options, statefile => 1, force_new_perfdata => 1); + bless $self, $class; + + $options{options}->add_options(arguments => { + "filter-edge-name:s" => { name => 'filter_edge_name' }, + "filter-edge-id:s" => { name => 'filter_edge_id' }, + "filter-link-id:s" => { name => 'filter_link_id' }, + "warning-status:s" => { name => 'warning_status', default => '' }, + "critical-status:s" => { name => 'critical_status', default => '' }, + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + $self->change_macros(macros => ['warning_status', 'critical_status']); +} + +sub manage_selection { + my ($self, %options) = @_; + + $self->{edges} = {}; + + $self->{cache_name} = "velocloud_" . $self->{mode} . '_' . $options{custom}->get_connection_infos() . '_' . + (defined($self->{option_results}->{filter_counters}) ? md5_hex($self->{option_results}->{filter_counters}) : md5_hex('all')) . '_' . + (defined($self->{option_results}->{filter_name}) ? md5_hex($self->{option_results}->{filter_name}) : md5_hex('all')) . '_' . + (defined($self->{option_results}->{filter_id}) ? md5_hex($self->{option_results}->{filter_id}) : md5_hex('all')); + + my $results = $options{custom}->list_edges; + + foreach my $edge (@{$results}) { + if (defined($self->{option_results}->{filter_edge_name}) && $self->{option_results}->{filter_edge_name} ne '' && + $edge->{name} !~ /$self->{option_results}->{filter_edge_name}/) { + $self->{output}->output_add(long_msg => "skipping '" . $edge->{name} . "'.", debug => 1); + next; + } + if (defined($self->{option_results}->{filter_edge_id}) && $self->{option_results}->{filter_edge_id} ne '' && + $edge->{id} !~ /$self->{option_results}->{filter_edge_id}/) { + $self->{output}->output_add(long_msg => "skipping '" . $edge->{id} . "'.", debug => 1); + next; + } + + $self->{edges}->{$edge->{id}}->{id} = $edge->{id}; + $self->{edges}->{$edge->{id}}->{display} = $edge->{name}; + + my $links = $options{custom}->list_links(edge_id => $edge->{id}); + + foreach my $link (@{$links}) { + if (defined($self->{option_results}->{filter_link_id}) && $self->{option_results}->{filter_link_id} ne '' && + $link->{linkId} !~ /$self->{option_results}->{filter_link_id}/) { + $self->{output}->output_add(long_msg => "skipping '" . $edge->{id} . "'.", debug => 1); + next; + } + + $self->{edges}->{$edge->{id}}->{links}->{$link->{linkId}} = { + display => $link->{link}->{displayName}, + id => $link->{linkId}, + interface => $link->{link}->{interface}, + state => $link->{link}->{state}, + vpn_state => $link->{link}->{vpnState}, + traffic_out => $link->{bytesTx} * 8, + traffic_in => $link->{bytesRx} * 8, + latency_out => $link->{bestLatencyMsTx}, + latency_in => $link->{bestLatencyMsRx}, + jitter_out => $link->{bestJitterMsTx}, + jitter_in => $link->{bestJitterMsRx}, + packet_loss_out => $link->{bestLossPctTx}, + packet_loss_in => $link->{bestLossPctRx}, + }; + } + } + + if (scalar(keys %{$self->{edges}}) <= 0) { + $self->{output}->add_option_msg(short_msg => "No edge found."); + $self->{output}->option_exit(); + } +} + +1; + +__END__ + +=head1 MODE + +Check edge links. + +=over 8 + +=item B<--filter-edge-name> + +Filter edge by name (Can be a regexp). + +=item B<--filter-edge-id> + +Filter edge by id (Can be a regexp). + +=item B<--filter-link-id> + +Filter link by id (Can be a regexp). + +=item B<--warning-status> + +Set warning threshold for status (Default: ''). +Can used special variables like: %{state}, %{vpn_state}. + +=item B<--critical-status> + +Set critical threshold for status (Default: ''). +Can used special variables like: %{state}, %{vpn_state}. + +=item B<--warning-*> + +Threshold warning. +Can be: 'traffic-in', 'traffic-out'. + +=item B<--critical-*> + +Threshold critical. +Can be: 'traffic-in', 'traffic-out'. + +=back + +=cut diff --git a/cloud/vmware/velocloud/restapi/mode/listedges.pm b/cloud/vmware/velocloud/restapi/mode/listedges.pm index 43c0ccdcb..99535d4ff 100644 --- a/cloud/vmware/velocloud/restapi/mode/listedges.pm +++ b/cloud/vmware/velocloud/restapi/mode/listedges.pm @@ -51,8 +51,10 @@ sub run { $self->manage_selection(%options); foreach my $edge (@{$self->{edges}}) { - $self->{output}->output_add(long_msg => sprintf("[id = %s][name = %s][edge_state = %s][service_state = %s][ha_state = %s][activation_state = %s]", - $edge->{id}, $edge->{name}, $edge->{edgeState}, $edge->{serviceState}, $edge->{haState}, $edge->{activationState})); + $self->{output}->output_add(long_msg => sprintf("[id = %s][name = %s][description = %s][edge_state = %s]" . + "[service_state = %s][ha_state = %s][activation_state = %s]", + $edge->{id}, $edge->{name}, $edge->{description}, $edge->{edgeState}, $edge->{serviceState}, + $edge->{haState}, $edge->{activationState})); } $self->{output}->output_add(severity => 'OK', @@ -64,7 +66,8 @@ sub run { sub disco_format { my ($self, %options) = @_; - $self->{output}->add_disco_format(elements => ['id', 'name', 'edge_state', 'service_state', 'ha_state', 'activation_state']); + $self->{output}->add_disco_format(elements => ['id', 'name', 'description', 'edge_state', 'service_state', + 'ha_state', 'activation_state']); } sub disco_show { @@ -75,6 +78,7 @@ sub disco_show { $self->{output}->add_disco_entry( id => $edge->{id}, name => $edge->{name}, + description => $edge->{description}, edge_state => $edge->{edgeState}, service_state => $edge->{serviceState}, ha_state => $edge->{haState}, diff --git a/cloud/vmware/velocloud/restapi/plugin.pm b/cloud/vmware/velocloud/restapi/plugin.pm index 1187741cf..d7dbf9402 100644 --- a/cloud/vmware/velocloud/restapi/plugin.pm +++ b/cloud/vmware/velocloud/restapi/plugin.pm @@ -31,6 +31,9 @@ sub new { $self->{version} = '0.1'; %{$self->{modes}} = ( + 'discovery' => 'cloud::vmware::velocloud::restapi::mode::discovery', + 'edge-status' => 'cloud::vmware::velocloud::restapi::mode::edgestatus', + 'link-status' => 'cloud::vmware::velocloud::restapi::mode::linkstatus', 'list-edges' => 'cloud::vmware::velocloud::restapi::mode::listedges', );