enh velocloud

This commit is contained in:
Colin Gagnaire 2019-07-02 18:25:48 +02:00
parent 74cc338d73
commit 00af42a47f
6 changed files with 603 additions and 10 deletions

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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},

View File

@ -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',
);