enh velocloud
This commit is contained in:
parent
51fbf737f7
commit
7081780872
|
@ -24,6 +24,7 @@ use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
use centreon::plugins::http;
|
use centreon::plugins::http;
|
||||||
use JSON::XS;
|
use JSON::XS;
|
||||||
|
use DateTime;
|
||||||
|
|
||||||
sub new {
|
sub new {
|
||||||
my ($class, %options) = @_;
|
my ($class, %options) = @_;
|
||||||
|
@ -48,6 +49,7 @@ sub new {
|
||||||
"password:s" => { name => 'password' },
|
"password:s" => { name => 'password' },
|
||||||
"operator-user" => { name => 'operator_user' },
|
"operator-user" => { name => 'operator_user' },
|
||||||
"api-path:s" => { name => 'api_path' },
|
"api-path:s" => { name => 'api_path' },
|
||||||
|
"timeframe:s" => { name => 'timeframe' },
|
||||||
"timeout:s" => { name => 'timeout' },
|
"timeout:s" => { name => 'timeout' },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -220,11 +222,11 @@ sub request_api {
|
||||||
}
|
}
|
||||||
|
|
||||||
$self->{output}->output_add(long_msg => "URL: '" . $self->{proto} . '://' . $self->{hostname} . ':' .
|
$self->{output}->output_add(long_msg => "URL: '" . $self->{proto} . '://' . $self->{hostname} . ':' .
|
||||||
$self->{port} . $options{url_path} . "'", debug => 1);
|
$self->{port} . $self->{api_path} . $options{path} . "'", debug => 1);
|
||||||
|
|
||||||
my $content = $self->{http}->request(
|
my $content = $self->{http}->request(
|
||||||
method => $options{method},
|
method => $options{method},
|
||||||
url_path => $options{url_path},
|
url_path => $self->{api_path} . $options{path},
|
||||||
query_form_post => $encoded_form_post,
|
query_form_post => $encoded_form_post,
|
||||||
critical_status => '', warning_status => '', unknown_status => ''
|
critical_status => '', warning_status => '', unknown_status => ''
|
||||||
);
|
);
|
||||||
|
@ -255,7 +257,7 @@ sub list_edges {
|
||||||
|
|
||||||
my $response = $self->request_api(
|
my $response = $self->request_api(
|
||||||
method => 'POST',
|
method => 'POST',
|
||||||
url_path => $self->{api_path} . '/enterprise/getEnterpriseEdges',
|
path => '/enterprise/getEnterpriseEdges',
|
||||||
query_form_post => { enterpriseId => $self->{entreprise_id} }
|
query_form_post => { enterpriseId => $self->{entreprise_id} }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -264,14 +266,112 @@ sub list_edges {
|
||||||
|
|
||||||
sub list_links {
|
sub list_links {
|
||||||
my ($self, %options) = @_;
|
my ($self, %options) = @_;
|
||||||
|
|
||||||
my $response = $self->request_api(
|
if (!defined($self->{entreprise_id})) {
|
||||||
|
$self->get_entreprise_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
my $results = $self->request_api(
|
||||||
method => 'POST',
|
method => 'POST',
|
||||||
url_path => $self->{api_path} . '/metrics/getEdgeLinkMetrics',
|
path => '/metrics/getEdgeLinkMetrics',
|
||||||
query_form_post => { id => $options{edge_id} }
|
query_form_post => {
|
||||||
|
enterpriseId => $self->{entreprise_id},
|
||||||
|
edgeId => $options{edge_id},
|
||||||
|
metrics => [ 'bytesRx' ],
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return $response;
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub get_links_metrics {
|
||||||
|
my ($self, %options) = @_;
|
||||||
|
|
||||||
|
if (!defined($self->{entreprise_id})) {
|
||||||
|
$self->get_entreprise_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
my $start_time = DateTime->now->subtract(seconds => $options{timeframe})->iso8601.'Z';
|
||||||
|
|
||||||
|
my $results = $self->request_api(
|
||||||
|
method => 'POST',
|
||||||
|
path => '/metrics/getEdgeLinkMetrics',
|
||||||
|
query_form_post => {
|
||||||
|
enterpriseId => $self->{entreprise_id},
|
||||||
|
edgeId => $options{edge_id},
|
||||||
|
metrics => [ 'bytesRx', 'bytesTx', 'bestJitterMsRx', 'bestJitterMsTx',
|
||||||
|
'bestLatencyMsRx', 'bestLatencyMsTx', 'bestLossPctRx', 'bestLossPctTx' ],
|
||||||
|
interval => {
|
||||||
|
start => $start_time
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub get_apps_metrics {
|
||||||
|
my ($self, %options) = @_;
|
||||||
|
|
||||||
|
my $start_time = DateTime->now->subtract(seconds => $options{timeframe})->iso8601.'Z';
|
||||||
|
|
||||||
|
my $results = $self->request_api(
|
||||||
|
method => 'POST',
|
||||||
|
path => '/metrics/getEdgeAppMetrics',
|
||||||
|
query_form_post => {
|
||||||
|
enterpriseId => $self->{entreprise_id},
|
||||||
|
edgeId => $options{edge_id},
|
||||||
|
metrics => [ 'bytesRx', 'bytesTx', 'packetsRx', 'packetsTx' ],
|
||||||
|
interval => {
|
||||||
|
start => $start_time
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub get_links_qoe {
|
||||||
|
my ($self, %options) = @_;
|
||||||
|
|
||||||
|
my $start_time = DateTime->now->subtract(seconds => $options{timeframe})->iso8601.'Z';
|
||||||
|
|
||||||
|
my $results = $self->request_api(
|
||||||
|
method => 'POST',
|
||||||
|
path => '/linkQualityEvent/getLinkQualityEvents',
|
||||||
|
query_form_post => {
|
||||||
|
enterpriseId => $self->{entreprise_id},
|
||||||
|
edgeId => $options{edge_id},
|
||||||
|
debug => 'false',
|
||||||
|
individualScores => 'false',
|
||||||
|
maxSamples => '15',
|
||||||
|
interval => {
|
||||||
|
start => $start_time
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub get_categories_metrics {
|
||||||
|
my ($self, %options) = @_;
|
||||||
|
|
||||||
|
my $start_time = DateTime->now->subtract(seconds => $options{timeframe})->iso8601.'Z';
|
||||||
|
|
||||||
|
my $results = $self->request_api(
|
||||||
|
method => 'POST',
|
||||||
|
path => '/metrics/getEdgeCategoryMetrics',
|
||||||
|
query_form_post => {
|
||||||
|
id => $options{edge_id},
|
||||||
|
metrics => [ 'bytesRx', 'bytesTx', 'packetsRx', 'packetsTx' ],
|
||||||
|
interval => {
|
||||||
|
start => $start_time
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return $results;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub DESTROY {
|
sub DESTROY {
|
||||||
|
@ -326,6 +426,10 @@ Set if the user is an operator.
|
||||||
|
|
||||||
API base url path (Default: '/portal/rest').
|
API base url path (Default: '/portal/rest').
|
||||||
|
|
||||||
|
=item B<--timeframe>
|
||||||
|
|
||||||
|
Set timeframe in seconds (Default: 900).
|
||||||
|
|
||||||
=item B<--timeout>
|
=item B<--timeout>
|
||||||
|
|
||||||
Set HTTP timeout in seconds (Default: '10').
|
Set HTTP timeout in seconds (Default: '10').
|
||||||
|
|
|
@ -0,0 +1,204 @@
|
||||||
|
#
|
||||||
|
# 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::applicationusage;
|
||||||
|
|
||||||
|
use base qw(centreon::plugins::templates::counter);
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
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 applications usage are ok', indent_long_output => ' ',
|
||||||
|
group => [
|
||||||
|
{ name => 'apps', display_long => 1, cb_prefix_output => 'prefix_app_output',
|
||||||
|
message_multiple => 'All applications usage are ok', type => 1 },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
$self->{maps_counters}->{apps} = [
|
||||||
|
{ label => 'traffic-in', nlabel => 'application.traffic.in.bitspersecond', set => {
|
||||||
|
key_values => [ { name => 'traffic_in' }, { name => 'display' }, { name => 'id' } ],
|
||||||
|
output_change_bytes => 2,
|
||||||
|
output_template => 'Traffic In: %s %s/s',
|
||||||
|
perfdatas => [
|
||||||
|
{ value => 'traffic_in_absolute', template => '%s',
|
||||||
|
min => 0, unit => 'b/s', label_extra_instance => 1 },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ label => 'traffic-out', nlabel => 'application.traffic.out.bitspersecond', set => {
|
||||||
|
key_values => [ { name => 'traffic_out' }, { name => 'display' }, { name => 'id' } ],
|
||||||
|
output_change_bytes => 2,
|
||||||
|
output_template => 'Traffic Out: %s %s/s',
|
||||||
|
perfdatas => [
|
||||||
|
{ value => 'traffic_out_absolute', template => '%s',
|
||||||
|
min => 0, unit => 'b/s', label_extra_instance => 1 },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ label => 'packets-in', nlabel => 'application.packets.in.persecond', set => {
|
||||||
|
key_values => [ { name => 'packets_in' }, { name => 'display' }, { name => 'id' } ],
|
||||||
|
output_template => 'Packets In: %.2f packets/s',
|
||||||
|
perfdatas => [
|
||||||
|
{ value => 'packets_in_absolute', template => '%.2f',
|
||||||
|
min => 0, unit => 'packets/s', label_extra_instance => 1 },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ label => 'packets-out', nlabel => 'application.packets.out.persecond', set => {
|
||||||
|
key_values => [ { name => 'packets_out' }, { name => 'display' }, { name => 'id' } ],
|
||||||
|
output_template => 'Packets Out: %.2f packets/s',
|
||||||
|
perfdatas => [
|
||||||
|
{ value => 'packets_out_absolute', template => '%.2f',
|
||||||
|
min => 0, unit => 'packets/s', label_extra_instance => 1 },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
sub prefix_edge_output {
|
||||||
|
my ($self, %options) = @_;
|
||||||
|
|
||||||
|
return "Edge '" . $options{instance_value}->{display} . "' ";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub prefix_app_output {
|
||||||
|
my ($self, %options) = @_;
|
||||||
|
|
||||||
|
return "Application '" . $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, force_new_perfdata => 1);
|
||||||
|
bless $self, $class;
|
||||||
|
|
||||||
|
$options{options}->add_options(arguments => {
|
||||||
|
"filter-edge-name:s" => { name => 'filter_edge_name' },
|
||||||
|
"filter-application-name:s" => { name => 'filter_application_name' },
|
||||||
|
"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->{timeframe} = defined($self->{option_results}->{timeframe}) ? $self->{option_results}->{timeframe} : 900;
|
||||||
|
|
||||||
|
$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_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
$self->{edges}->{$edge->{name}}->{id} = $edge->{id};
|
||||||
|
$self->{edges}->{$edge->{name}}->{display} = $edge->{name};
|
||||||
|
|
||||||
|
my $apps = $options{custom}->get_apps_metrics(
|
||||||
|
edge_id => $edge->{id},
|
||||||
|
timeframe => $self->{timeframe}
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach my $app (@{$apps}) {
|
||||||
|
if (defined($self->{option_results}->{filter_application_name}) &&
|
||||||
|
$self->{option_results}->{filter_application_name} ne '' &&
|
||||||
|
$app->{name} !~ /$self->{option_results}->{filter_application_name}/) {
|
||||||
|
$self->{output}->output_add(long_msg => "skipping '" . $edge->{id} . "'.", debug => 1);
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
$self->{edges}->{$edge->{name}}->{apps}->{$app->{name}} = {
|
||||||
|
id => $app->{application},
|
||||||
|
display => $app->{name},
|
||||||
|
traffic_out => int($app->{bytesTx} * 8 / $self->{timeframe}),
|
||||||
|
traffic_in => int($app->{bytesRx} * 8 / $self->{timeframe}),
|
||||||
|
packets_out => $app->{packetsTx} / $self->{timeframe},
|
||||||
|
packets_in => $app->{packetsRx} / $self->{timeframe},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 applications usage per edges.
|
||||||
|
|
||||||
|
=over 8
|
||||||
|
|
||||||
|
=item B<--filter-edge-name>
|
||||||
|
|
||||||
|
Filter edge by name (Can be a regexp).
|
||||||
|
|
||||||
|
=item B<--filter-application-name>
|
||||||
|
|
||||||
|
Filter application by name (Can be a regexp).
|
||||||
|
|
||||||
|
=item B<--warning-*>
|
||||||
|
|
||||||
|
Threshold warning.
|
||||||
|
Can be: 'traffic-in', 'traffic-out',
|
||||||
|
'packets-in', 'packets-out'.
|
||||||
|
|
||||||
|
=item B<--critical-*>
|
||||||
|
|
||||||
|
Threshold critical.
|
||||||
|
Can be: 'traffic-in', 'traffic-out',
|
||||||
|
'packets-in', 'packets-out'.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=cut
|
|
@ -0,0 +1,204 @@
|
||||||
|
#
|
||||||
|
# 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::categoryusage;
|
||||||
|
|
||||||
|
use base qw(centreon::plugins::templates::counter);
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
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 categories usage are ok', indent_long_output => ' ',
|
||||||
|
group => [
|
||||||
|
{ name => 'categories', display_long => 1, cb_prefix_output => 'prefix_category_output',
|
||||||
|
message_multiple => 'All categories usage are ok', type => 1 },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
$self->{maps_counters}->{categories} = [
|
||||||
|
{ label => 'traffic-in', nlabel => 'category.traffic.in.bitspersecond', set => {
|
||||||
|
key_values => [ { name => 'traffic_in' }, { name => 'display' }, { name => 'id' } ],
|
||||||
|
output_change_bytes => 2,
|
||||||
|
output_template => 'Traffic In: %s %s/s',
|
||||||
|
perfdatas => [
|
||||||
|
{ value => 'traffic_in_absolute', template => '%s',
|
||||||
|
min => 0, unit => 'b/s', label_extra_instance => 1 },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ label => 'traffic-out', nlabel => 'category.traffic.out.bitspersecond', set => {
|
||||||
|
key_values => [ { name => 'traffic_out' }, { name => 'display' }, { name => 'id' } ],
|
||||||
|
output_change_bytes => 2,
|
||||||
|
output_template => 'Traffic Out: %s %s/s',
|
||||||
|
perfdatas => [
|
||||||
|
{ value => 'traffic_out_absolute', template => '%s',
|
||||||
|
min => 0, unit => 'b/s', label_extra_instance => 1 },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ label => 'packets-in', nlabel => 'category.packets.in.persecond', set => {
|
||||||
|
key_values => [ { name => 'packets_in' }, { name => 'display' }, { name => 'id' } ],
|
||||||
|
output_template => 'Packets In: %.2f packets/s',
|
||||||
|
perfdatas => [
|
||||||
|
{ value => 'packets_in_absolute', template => '%.2f',
|
||||||
|
min => 0, unit => 'packets/s', label_extra_instance => 1 },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ label => 'packets-out', nlabel => 'category.packets.out.persecond', set => {
|
||||||
|
key_values => [ { name => 'packets_out' }, { name => 'display' }, { name => 'id' } ],
|
||||||
|
output_template => 'Packets Out: %.2f packets/s',
|
||||||
|
perfdatas => [
|
||||||
|
{ value => 'packets_out_absolute', template => '%.2f',
|
||||||
|
min => 0, unit => 'packets/s', label_extra_instance => 1 },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
sub prefix_edge_output {
|
||||||
|
my ($self, %options) = @_;
|
||||||
|
|
||||||
|
return "Edge '" . $options{instance_value}->{display} . "' ";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub prefix_category_output {
|
||||||
|
my ($self, %options) = @_;
|
||||||
|
|
||||||
|
return "Category '" . $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, force_new_perfdata => 1);
|
||||||
|
bless $self, $class;
|
||||||
|
|
||||||
|
$options{options}->add_options(arguments => {
|
||||||
|
"filter-edge-name:s" => { name => 'filter_edge_name' },
|
||||||
|
"filter-category-name:s" => { name => 'filter_category_name' },
|
||||||
|
"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->{timeframe} = defined($self->{option_results}->{timeframe}) ? $self->{option_results}->{timeframe} : 900;
|
||||||
|
|
||||||
|
$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_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
$self->{edges}->{$edge->{name}}->{id} = $edge->{id};
|
||||||
|
$self->{edges}->{$edge->{name}}->{display} = $edge->{name};
|
||||||
|
|
||||||
|
my $categories = $options{custom}->get_categories_metrics(
|
||||||
|
edge_id => $edge->{id},
|
||||||
|
timeframe => $self->{timeframe}
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach my $category (@{$categories}) {
|
||||||
|
if (defined($self->{option_results}->{filter_category_name}) &&
|
||||||
|
$self->{option_results}->{filter_category_name} ne '' &&
|
||||||
|
$category->{name} !~ /$self->{option_results}->{filter_category_name}/) {
|
||||||
|
$self->{output}->output_add(long_msg => "skipping '" . $edge->{id} . "'.", debug => 1);
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
$self->{edges}->{$edge->{name}}->{categories}->{$category->{name}} = {
|
||||||
|
id => $category->{category},
|
||||||
|
display => $category->{name},
|
||||||
|
traffic_out => int($category->{bytesTx} * 8 / $self->{timeframe}),
|
||||||
|
traffic_in => int($category->{bytesRx} * 8 / $self->{timeframe}),
|
||||||
|
packets_out => $category->{packetsTx} / $self->{timeframe},
|
||||||
|
packets_in => $category->{packetsRx} / $self->{timeframe},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 categories usage per edges.
|
||||||
|
|
||||||
|
=over 8
|
||||||
|
|
||||||
|
=item B<--filter-edge-name>
|
||||||
|
|
||||||
|
Filter edge by name (Can be a regexp).
|
||||||
|
|
||||||
|
=item B<--filter-category-name>
|
||||||
|
|
||||||
|
Filter category by name (Can be a regexp).
|
||||||
|
|
||||||
|
=item B<--warning-*>
|
||||||
|
|
||||||
|
Threshold warning.
|
||||||
|
Can be: 'traffic-in', 'traffic-out',
|
||||||
|
'packets-in', 'packets-out'.
|
||||||
|
|
||||||
|
=item B<--critical-*>
|
||||||
|
|
||||||
|
Threshold critical.
|
||||||
|
Can be: 'traffic-in', 'traffic-out',
|
||||||
|
'packets-in', 'packets-out'.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=cut
|
|
@ -0,0 +1,231 @@
|
||||||
|
#
|
||||||
|
# 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::edgeqoe;
|
||||||
|
|
||||||
|
use base qw(centreon::plugins::templates::counter);
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
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 QOE are ok', indent_long_output => ' ',
|
||||||
|
group => [
|
||||||
|
{ name => 'global', type => 0 },
|
||||||
|
{ name => 'links', display_long => 1, cb_prefix_output => 'prefix_link_output',
|
||||||
|
message_multiple => 'All links QOE are ok', type => 1 },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
$self->{maps_counters}->{global} = [
|
||||||
|
{ label => 'qoe-voice', nlabel => 'qoe.voice.count', set => {
|
||||||
|
key_values => [ { name => 'voice' } ],
|
||||||
|
output_template => 'Voice QOE: %s',
|
||||||
|
perfdatas => [
|
||||||
|
{ value => 'voice_absolute', template => '%s',
|
||||||
|
min => 0, max => 10, label_extra_instance => 1 },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ label => 'qoe-video', nlabel => 'qoe.video.count', set => {
|
||||||
|
key_values => [ { name => 'video' } ],
|
||||||
|
output_template => 'Video QOE: %s',
|
||||||
|
perfdatas => [
|
||||||
|
{ value => 'video_absolute', template => '%s',
|
||||||
|
min => 0, max => 10, label_extra_instance => 1 },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ label => 'qoe-transactional', nlabel => 'qoe.transactional.count', set => {
|
||||||
|
key_values => [ { name => 'transactional' } ],
|
||||||
|
output_template => 'Transactional QOE: %s',
|
||||||
|
perfdatas => [
|
||||||
|
{ value => 'transactional_absolute', template => '%s',
|
||||||
|
min => 0, max => 10, label_extra_instance => 1 },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
];
|
||||||
|
$self->{maps_counters}->{links} = [
|
||||||
|
{ label => 'qoe-voice', nlabel => 'link.qoe.voice.count', set => {
|
||||||
|
key_values => [ { name => 'voice' }, { name => 'display' }, { name => 'id' } ],
|
||||||
|
output_template => 'Voice QOE: %s',
|
||||||
|
perfdatas => [
|
||||||
|
{ value => 'voice_absolute', template => '%s',
|
||||||
|
min => 0, max => 10, label_extra_instance => 1 },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ label => 'qoe-video', nlabel => 'link.qoe.video.count', set => {
|
||||||
|
key_values => [ { name => 'video' }, { name => 'display' }, { name => 'id' } ],
|
||||||
|
output_template => 'Video QOE: %s',
|
||||||
|
perfdatas => [
|
||||||
|
{ value => 'video_absolute', template => '%s',
|
||||||
|
min => 0, max => 10, label_extra_instance => 1 },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ label => 'qoe-transactional', nlabel => 'link.qoe.transactional.count', set => {
|
||||||
|
key_values => [ { name => 'transactional' }, { name => 'display' }, { name => 'id' } ],
|
||||||
|
output_template => 'Transactional QOE: %s',
|
||||||
|
perfdatas => [
|
||||||
|
{ value => 'transactional_absolute', template => '%s',
|
||||||
|
min => 0, max => 10, label_extra_instance => 1 },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
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, force_new_perfdata => 1);
|
||||||
|
bless $self, $class;
|
||||||
|
|
||||||
|
$options{options}->add_options(arguments => {
|
||||||
|
"filter-edge-name:s" => { name => 'filter_edge_name' },
|
||||||
|
"filter-link-name:s" => { name => 'filter_link_name' },
|
||||||
|
});
|
||||||
|
|
||||||
|
return $self;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub check_options {
|
||||||
|
my ($self, %options) = @_;
|
||||||
|
$self->SUPER::check_options(%options);
|
||||||
|
|
||||||
|
$self->{timeframe} = defined($self->{option_results}->{timeframe}) ? $self->{option_results}->{timeframe} : 900;
|
||||||
|
|
||||||
|
$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_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
$self->{edges}->{$edge->{name}}->{id} = $edge->{id};
|
||||||
|
$self->{edges}->{$edge->{name}}->{display} = $edge->{name};
|
||||||
|
|
||||||
|
my $links = $options{custom}->list_links(
|
||||||
|
edge_id => $edge->{id}
|
||||||
|
);
|
||||||
|
|
||||||
|
my $qoes = $options{custom}->get_links_qoe(
|
||||||
|
edge_id => $edge->{id},
|
||||||
|
timeframe => $self->{timeframe}
|
||||||
|
);
|
||||||
|
|
||||||
|
next if (ref($qoes) ne 'HASH');
|
||||||
|
|
||||||
|
$self->{edges}->{$edge->{name}}->{global} = {
|
||||||
|
voice => $qoes->{overallLinkQuality}->{score}->{0},
|
||||||
|
video => $qoes->{overallLinkQuality}->{score}->{1},
|
||||||
|
transactional => $qoes->{overallLinkQuality}->{score}->{2},
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach my $link (@{$links}) {
|
||||||
|
next if (!defined($qoes->{$link->{link}->{internalId}}));
|
||||||
|
|
||||||
|
if (defined($self->{option_results}->{filter_link_name}) && $self->{option_results}->{filter_link_name} ne '' &&
|
||||||
|
$link->{link}->{displayName} !~ /$self->{option_results}->{filter_link_name}/) {
|
||||||
|
$self->{output}->output_add(long_msg => "skipping '" . $edge->{id} . "'.", debug => 1);
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
$self->{edges}->{$edge->{name}}->{links}->{$link->{link}->{displayName}} = {
|
||||||
|
id => $link->{linkId},
|
||||||
|
display => $link->{link}->{displayName},
|
||||||
|
voice => $qoes->{$link->{link}->{internalId}}->{score}->{0},
|
||||||
|
video => $qoes->{$link->{link}->{internalId}}->{score}->{1},
|
||||||
|
transactional => $qoes->{$link->{link}->{internalId}}->{score}->{2},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 links QOE before and global QOE after VeloCloud Enhancements.
|
||||||
|
|
||||||
|
=over 8
|
||||||
|
|
||||||
|
=item B<--filter-edge-name>
|
||||||
|
|
||||||
|
Filter edge by name (Can be a regexp).
|
||||||
|
|
||||||
|
=item B<--filter-link-name>
|
||||||
|
|
||||||
|
Filter link by name (Can be a regexp).
|
||||||
|
|
||||||
|
=item B<--warning-*>
|
||||||
|
|
||||||
|
Threshold warning.
|
||||||
|
Can be: 'qoe-voice', 'qoe-video', 'qoe-transactional'.
|
||||||
|
|
||||||
|
=item B<--critical-*>
|
||||||
|
|
||||||
|
Threshold critical.
|
||||||
|
Can be: 'qoe-voice', 'qoe-video', 'qoe-transactional'.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=cut
|
|
@ -81,7 +81,6 @@ sub new {
|
||||||
|
|
||||||
$options{options}->add_options(arguments => {
|
$options{options}->add_options(arguments => {
|
||||||
"filter-name:s" => { name => 'filter_name' },
|
"filter-name:s" => { name => 'filter_name' },
|
||||||
"filter-id:s" => { name => 'filter_id' },
|
|
||||||
"warning-status:s" => { name => 'warning_status', default => '' },
|
"warning-status:s" => { name => 'warning_status', default => '' },
|
||||||
"critical-status:s" => { name => 'critical_status', default => '' },
|
"critical-status:s" => { name => 'critical_status', default => '' },
|
||||||
});
|
});
|
||||||
|
@ -109,11 +108,6 @@ sub manage_selection {
|
||||||
$self->{output}->output_add(long_msg => "skipping '" . $edge->{name} . "'.", debug => 1);
|
$self->{output}->output_add(long_msg => "skipping '" . $edge->{name} . "'.", debug => 1);
|
||||||
next;
|
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}} = {
|
$self->{edges}->{$edge->{id}} = {
|
||||||
display => $edge->{name},
|
display => $edge->{name},
|
||||||
|
@ -144,10 +138,6 @@ Check edge status.
|
||||||
|
|
||||||
Filter edge by name (Can be a regexp).
|
Filter edge by name (Can be a regexp).
|
||||||
|
|
||||||
=item B<--filter-id>
|
|
||||||
|
|
||||||
Filter edge by id (Can be a regexp).
|
|
||||||
|
|
||||||
=item B<--warning-status>
|
=item B<--warning-status>
|
||||||
|
|
||||||
Set warning threshold for status (Default: '').
|
Set warning threshold for status (Default: '').
|
||||||
|
|
|
@ -25,7 +25,6 @@ use base qw(centreon::plugins::templates::counter);
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold);
|
use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold);
|
||||||
use Digest::MD5 qw(md5_hex);
|
|
||||||
|
|
||||||
sub custom_status_output {
|
sub custom_status_output {
|
||||||
my ($self, %options) = @_;
|
my ($self, %options) = @_;
|
||||||
|
@ -37,7 +36,6 @@ sub custom_status_output {
|
||||||
sub custom_status_calc {
|
sub custom_status_calc {
|
||||||
my ($self, %options) = @_;
|
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}->{state} = $options{new_datas}->{$self->{instance} . '_state'};
|
||||||
$self->{result_values}->{vpn_state} = $options{new_datas}->{$self->{instance} . '_vpn_state'};
|
$self->{result_values}->{vpn_state} = $options{new_datas}->{$self->{instance} . '_vpn_state'};
|
||||||
$self->{result_values}->{display} = $options{new_datas}->{$self->{instance} . '_display'};
|
$self->{result_values}->{display} = $options{new_datas}->{$self->{instance} . '_display'};
|
||||||
|
@ -50,7 +48,7 @@ sub set_counters {
|
||||||
|
|
||||||
$self->{maps_counters_type} = [
|
$self->{maps_counters_type} = [
|
||||||
{ name => 'edges', type => 3, cb_prefix_output => 'prefix_edge_output', cb_long_output => 'long_output',
|
{ 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 => ' ',
|
message_multiple => 'All edges links status are ok', indent_long_output => ' ',
|
||||||
group => [
|
group => [
|
||||||
{ name => 'links', display_long => 1, cb_prefix_output => 'prefix_link_output',
|
{ name => 'links', display_long => 1, cb_prefix_output => 'prefix_link_output',
|
||||||
message_multiple => 'All links status are ok', type => 1 },
|
message_multiple => 'All links status are ok', type => 1 },
|
||||||
|
@ -60,7 +58,7 @@ sub set_counters {
|
||||||
|
|
||||||
$self->{maps_counters}->{links} = [
|
$self->{maps_counters}->{links} = [
|
||||||
{ label => 'status', set => {
|
{ label => 'status', set => {
|
||||||
key_values => [ { name => 'interface' }, { name => 'state' }, { name => 'vpn_state' },
|
key_values => [ { name => 'state' }, { name => 'vpn_state' },
|
||||||
{ name => 'display' }, { name => 'id' } ],
|
{ name => 'display' }, { name => 'id' } ],
|
||||||
closure_custom_calc => $self->can('custom_status_calc'),
|
closure_custom_calc => $self->can('custom_status_calc'),
|
||||||
closure_custom_output => $self->can('custom_status_output'),
|
closure_custom_output => $self->can('custom_status_output'),
|
||||||
|
@ -68,80 +66,6 @@ sub set_counters {
|
||||||
closure_custom_threshold_check => \&catalog_status_threshold,
|
closure_custom_threshold_check => \&catalog_status_threshold,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ label => 'traffic-in', nlabel => 'link.traffic.in.bitspersecond', set => {
|
|
||||||
key_values => [ { name => 'traffic_in', diff => 1 }, { name => 'id' } ],
|
|
||||||
per_second => 1, output_change_bytes => 2,
|
|
||||||
output_template => 'Traffic In: %s %s/s',
|
|
||||||
perfdatas => [
|
|
||||||
{ value => 'traffic_in_per_second', 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', diff => 1 }, { name => 'id' } ],
|
|
||||||
per_second => 1, output_change_bytes => 2,
|
|
||||||
output_template => 'Traffic Out: %s %s/s',
|
|
||||||
perfdatas => [
|
|
||||||
{ value => 'traffic_out_per_second', 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' },
|
|
||||||
],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,13 +89,12 @@ sub long_output {
|
||||||
|
|
||||||
sub new {
|
sub new {
|
||||||
my ($class, %options) = @_;
|
my ($class, %options) = @_;
|
||||||
my $self = $class->SUPER::new(package => __PACKAGE__, %options, statefile => 1, force_new_perfdata => 1);
|
my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1);
|
||||||
bless $self, $class;
|
bless $self, $class;
|
||||||
|
|
||||||
$options{options}->add_options(arguments => {
|
$options{options}->add_options(arguments => {
|
||||||
"filter-edge-name:s" => { name => 'filter_edge_name' },
|
"filter-edge-name:s" => { name => 'filter_edge_name' },
|
||||||
"filter-edge-id:s" => { name => 'filter_edge_id' },
|
"filter-link-name:s" => { name => 'filter_link_name' },
|
||||||
"filter-link-id:s" => { name => 'filter_link_id' },
|
|
||||||
"warning-status:s" => { name => 'warning_status', default => '' },
|
"warning-status:s" => { name => 'warning_status', default => '' },
|
||||||
"critical-status:s" => { name => 'critical_status', default => '' },
|
"critical-status:s" => { name => 'critical_status', default => '' },
|
||||||
});
|
});
|
||||||
|
@ -183,6 +106,8 @@ sub check_options {
|
||||||
my ($self, %options) = @_;
|
my ($self, %options) = @_;
|
||||||
$self->SUPER::check_options(%options);
|
$self->SUPER::check_options(%options);
|
||||||
|
|
||||||
|
$self->{timeframe} = defined($self->{option_results}->{timeframe}) ? $self->{option_results}->{timeframe} : 900;
|
||||||
|
|
||||||
$self->change_macros(macros => ['warning_status', 'critical_status']);
|
$self->change_macros(macros => ['warning_status', 'critical_status']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,11 +116,6 @@ sub manage_selection {
|
||||||
|
|
||||||
$self->{edges} = {};
|
$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;
|
my $results = $options{custom}->list_edges;
|
||||||
|
|
||||||
foreach my $edge (@{$results}) {
|
foreach my $edge (@{$results}) {
|
||||||
|
@ -204,38 +124,27 @@ sub manage_selection {
|
||||||
$self->{output}->output_add(long_msg => "skipping '" . $edge->{name} . "'.", debug => 1);
|
$self->{output}->output_add(long_msg => "skipping '" . $edge->{name} . "'.", debug => 1);
|
||||||
next;
|
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->{name}}->{id} = $edge->{id};
|
||||||
$self->{edges}->{$edge->{id}}->{display} = $edge->{name};
|
$self->{edges}->{$edge->{name}}->{display} = $edge->{name};
|
||||||
|
|
||||||
my $links = $options{custom}->list_links(edge_id => $edge->{id});
|
my $links = $options{custom}->get_links_metrics(
|
||||||
|
edge_id => $edge->{id},
|
||||||
|
timeframe => $self->{timeframe}
|
||||||
|
);
|
||||||
|
|
||||||
foreach my $link (@{$links}) {
|
foreach my $link (@{$links}) {
|
||||||
if (defined($self->{option_results}->{filter_link_id}) && $self->{option_results}->{filter_link_id} ne '' &&
|
if (defined($self->{option_results}->{filter_link_name}) && $self->{option_results}->{filter_link_name} ne '' &&
|
||||||
$link->{linkId} !~ /$self->{option_results}->{filter_link_id}/) {
|
$link->{link}->{displayName} !~ /$self->{option_results}->{filter_link_name}/) {
|
||||||
$self->{output}->output_add(long_msg => "skipping '" . $edge->{id} . "'.", debug => 1);
|
$self->{output}->output_add(long_msg => "skipping '" . $edge->{id} . "'.", debug => 1);
|
||||||
next;
|
next;
|
||||||
}
|
}
|
||||||
|
|
||||||
$self->{edges}->{$edge->{id}}->{links}->{$link->{linkId}} = {
|
$self->{edges}->{$edge->{name}}->{links}->{$link->{link}->{displayName}} = {
|
||||||
display => $link->{link}->{displayName},
|
|
||||||
id => $link->{linkId},
|
id => $link->{linkId},
|
||||||
interface => $link->{link}->{interface},
|
display => $link->{link}->{displayName},
|
||||||
state => $link->{link}->{state},
|
state => $link->{link}->{state},
|
||||||
vpn_state => $link->{link}->{vpnState},
|
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},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -252,7 +161,7 @@ __END__
|
||||||
|
|
||||||
=head1 MODE
|
=head1 MODE
|
||||||
|
|
||||||
Check edge links.
|
Check edge links status.
|
||||||
|
|
||||||
=over 8
|
=over 8
|
||||||
|
|
||||||
|
@ -260,13 +169,9 @@ Check edge links.
|
||||||
|
|
||||||
Filter edge by name (Can be a regexp).
|
Filter edge by name (Can be a regexp).
|
||||||
|
|
||||||
=item B<--filter-edge-id>
|
=item B<--filter-link-name>
|
||||||
|
|
||||||
Filter edge by id (Can be a regexp).
|
Filter link by name (Can be a regexp).
|
||||||
|
|
||||||
=item B<--filter-link-id>
|
|
||||||
|
|
||||||
Filter link by id (Can be a regexp).
|
|
||||||
|
|
||||||
=item B<--warning-status>
|
=item B<--warning-status>
|
||||||
|
|
||||||
|
@ -278,16 +183,6 @@ Can used special variables like: %{state}, %{vpn_state}.
|
||||||
Set critical threshold for status (Default: '').
|
Set critical threshold for status (Default: '').
|
||||||
Can used special variables like: %{state}, %{vpn_state}.
|
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
|
=back
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|
|
@ -0,0 +1,243 @@
|
||||||
|
#
|
||||||
|
# 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::linkusage;
|
||||||
|
|
||||||
|
use base qw(centreon::plugins::templates::counter);
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
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 usage 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 => 'traffic-in', nlabel => 'link.traffic.in.bitspersecond', set => {
|
||||||
|
key_values => [ { name => 'traffic_in' }, { name => 'display' }, { name => 'id' } ],
|
||||||
|
output_change_bytes => 2,
|
||||||
|
output_template => 'Traffic In: %s %s/s',
|
||||||
|
perfdatas => [
|
||||||
|
{ value => 'traffic_in_absolute', template => '%s',
|
||||||
|
min => 0, unit => 'b/s', label_extra_instance => 1 },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ label => 'traffic-out', nlabel => 'link.traffic.out.bitspersecond', set => {
|
||||||
|
key_values => [ { name => 'traffic_out' }, { name => 'display' }, { name => 'id' } ],
|
||||||
|
output_change_bytes => 2,
|
||||||
|
output_template => 'Traffic Out: %s %s/s',
|
||||||
|
perfdatas => [
|
||||||
|
{ value => 'traffic_out_absolute', template => '%s',
|
||||||
|
min => 0, unit => 'b/s', label_extra_instance => 1 },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ label => 'latency-in', nlabel => 'link.latency.in.milliseconds', set => {
|
||||||
|
key_values => [ { name => 'latency_in' }, { name => 'display' }, { name => 'id' } ],
|
||||||
|
output_template => 'Latency In: %.2f ms',
|
||||||
|
perfdatas => [
|
||||||
|
{ value => 'latency_in_absolute', template => '%.2f',
|
||||||
|
min => 0, unit => 'ms', label_extra_instance => 1 },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ label => 'latency-out', nlabel => 'link.latency.out.milliseconds', set => {
|
||||||
|
key_values => [ { name => 'latency_out' }, { name => 'display' }, { name => 'id' } ],
|
||||||
|
output_template => 'Latency Out: %.2f ms',
|
||||||
|
perfdatas => [
|
||||||
|
{ value => 'latency_out_absolute', template => '%.2f',
|
||||||
|
min => 0, unit => 'ms', label_extra_instance => 1 },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ label => 'jitter-in', nlabel => 'link.jitter.in.milliseconds', set => {
|
||||||
|
key_values => [ { name => 'jitter_in' }, { name => 'display' }, { name => 'id' } ],
|
||||||
|
output_template => 'Jitter In: %.2f ms',
|
||||||
|
perfdatas => [
|
||||||
|
{ value => 'jitter_in_absolute', template => '%.2f',
|
||||||
|
min => 0, unit => 'ms', label_extra_instance => 1 },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ label => 'jitter-out', nlabel => 'link.jitter.out.milliseconds', set => {
|
||||||
|
key_values => [ { name => 'jitter_out' }, { name => 'display' }, { name => 'id' } ],
|
||||||
|
output_template => 'Jitter Out: %.2f ms',
|
||||||
|
perfdatas => [
|
||||||
|
{ value => 'jitter_out_absolute', template => '%.2f',
|
||||||
|
min => 0, unit => 'ms', label_extra_instance => 1 },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ label => 'packet-loss-in', nlabel => 'link.packet.loss.in.percentage', set => {
|
||||||
|
key_values => [ { name => 'packet_loss_in' }, { name => 'display' }, { name => 'id' } ],
|
||||||
|
output_template => 'Packet Loss In: %.2f%%',
|
||||||
|
perfdatas => [
|
||||||
|
{ value => 'packet_loss_in_absolute', template => '%.2f',
|
||||||
|
min => 0, max => 100, unit => '%', label_extra_instance => 1 },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ label => 'packet-loss-out', nlabel => 'link.packet.loss.out.percentage', set => {
|
||||||
|
key_values => [ { name => 'packet_loss_out' }, { name => 'display' }, { name => 'id' } ],
|
||||||
|
output_template => 'Packet Loss Out: %.2f%%',
|
||||||
|
perfdatas => [
|
||||||
|
{ value => 'packet_loss_out_absolute', template => '%.2f',
|
||||||
|
min => 0, max => 100, unit => '%', label_extra_instance => 1 },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
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, force_new_perfdata => 1);
|
||||||
|
bless $self, $class;
|
||||||
|
|
||||||
|
$options{options}->add_options(arguments => {
|
||||||
|
"filter-edge-name:s" => { name => 'filter_edge_name' },
|
||||||
|
"filter-link-name:s" => { name => 'filter_link_name' },
|
||||||
|
});
|
||||||
|
|
||||||
|
return $self;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub check_options {
|
||||||
|
my ($self, %options) = @_;
|
||||||
|
$self->SUPER::check_options(%options);
|
||||||
|
|
||||||
|
$self->{timeframe} = defined($self->{option_results}->{timeframe}) ? $self->{option_results}->{timeframe} : 900;
|
||||||
|
|
||||||
|
$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_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
$self->{edges}->{$edge->{name}}->{id} = $edge->{id};
|
||||||
|
$self->{edges}->{$edge->{name}}->{display} = $edge->{name};
|
||||||
|
|
||||||
|
my $links = $options{custom}->get_links_metrics(
|
||||||
|
edge_id => $edge->{id},
|
||||||
|
timeframe => $self->{timeframe}
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach my $link (@{$links}) {
|
||||||
|
if (defined($self->{option_results}->{filter_link_name}) && $self->{option_results}->{filter_link_name} ne '' &&
|
||||||
|
$link->{link}->{displayName} !~ /$self->{option_results}->{filter_link_name}/) {
|
||||||
|
$self->{output}->output_add(long_msg => "skipping '" . $edge->{id} . "'.", debug => 1);
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
$self->{edges}->{$edge->{name}}->{links}->{$link->{link}->{displayName}} = {
|
||||||
|
id => $link->{linkId},
|
||||||
|
display => $link->{link}->{displayName},
|
||||||
|
traffic_out => int($link->{bytesTx} * 8 / $self->{timeframe}),
|
||||||
|
traffic_in => int($link->{bytesRx} * 8 / $self->{timeframe}),
|
||||||
|
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 links usage per edges.
|
||||||
|
|
||||||
|
=over 8
|
||||||
|
|
||||||
|
=item B<--filter-edge-name>
|
||||||
|
|
||||||
|
Filter edge by name (Can be a regexp).
|
||||||
|
|
||||||
|
=item B<--filter-link-name>
|
||||||
|
|
||||||
|
Filter link by name (Can be a regexp).
|
||||||
|
|
||||||
|
=item B<--warning-*>
|
||||||
|
|
||||||
|
Threshold warning.
|
||||||
|
Can be: 'traffic-in', 'traffic-out', 'latency-in',
|
||||||
|
'latency-out', 'jitter-in', 'jitter-out',
|
||||||
|
'packet-loss-in', 'packet-loss-out'.
|
||||||
|
|
||||||
|
=item B<--critical-*>
|
||||||
|
|
||||||
|
Threshold critical.
|
||||||
|
Can be: 'traffic-in', 'traffic-out', 'latency-in',
|
||||||
|
'latency-out', 'jitter-in', 'jitter-out',
|
||||||
|
'packet-loss-in', 'packet-loss-out'.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=cut
|
|
@ -31,10 +31,14 @@ sub new {
|
||||||
|
|
||||||
$self->{version} = '0.1';
|
$self->{version} = '0.1';
|
||||||
%{$self->{modes}} = (
|
%{$self->{modes}} = (
|
||||||
'discovery' => 'cloud::vmware::velocloud::restapi::mode::discovery',
|
'application-usage' => 'cloud::vmware::velocloud::restapi::mode::applicationusage',
|
||||||
'edge-status' => 'cloud::vmware::velocloud::restapi::mode::edgestatus',
|
'category-usage' => 'cloud::vmware::velocloud::restapi::mode::categoryusage',
|
||||||
'link-status' => 'cloud::vmware::velocloud::restapi::mode::linkstatus',
|
'discovery' => 'cloud::vmware::velocloud::restapi::mode::discovery',
|
||||||
'list-edges' => 'cloud::vmware::velocloud::restapi::mode::listedges',
|
'edge-qoe' => 'cloud::vmware::velocloud::restapi::mode::edgeqoe',
|
||||||
|
'edge-status' => 'cloud::vmware::velocloud::restapi::mode::edgestatus',
|
||||||
|
'link-status' => 'cloud::vmware::velocloud::restapi::mode::linkstatus',
|
||||||
|
'link-usage' => 'cloud::vmware::velocloud::restapi::mode::linkusage',
|
||||||
|
'list-edges' => 'cloud::vmware::velocloud::restapi::mode::listedges',
|
||||||
);
|
);
|
||||||
|
|
||||||
$self->{custom_modes}{api} = 'cloud::vmware::velocloud::restapi::custom::api';
|
$self->{custom_modes}{api} = 'cloud::vmware::velocloud::restapi::custom::api';
|
||||||
|
|
Loading…
Reference in New Issue