add(mode): new vpn tunnels mode to meraki api plugin + tests (#5084)

Co-authored-by: garnier-quentin <garnier.quentin@gmail.com>
REFS: CTOR-98
This commit is contained in:
omercier 2024-06-28 10:20:19 +02:00 committed by GitHub
parent 1333b452fb
commit 294fa2b394
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 939 additions and 25 deletions

View File

@ -241,16 +241,17 @@ sub write_cache_file {
sub get_cache_file_response { sub get_cache_file_response {
my ($self, %options) = @_; my ($self, %options) = @_;
my $cache_filename = 'cache_meraki_'
. md5_hex($self->{api_token} . '_' .(defined($self->{option_results}->{api_filter_orgs}) ?
$self->{option_results}->{api_filter_orgs} : '')
);
$self->{cache}->read( $self->{cache}->read(
statefile => 'cache_meraki_' . md5_hex( statefile => $cache_filename
$self->{api_token} . '_' .
(defined($self->{option_results}->{api_filter_orgs}) ? $self->{option_results}->{api_filter_orgs} : '')
)
); );
$self->{datas} = $self->{cache}->get(name => 'response'); $self->{datas} = $self->{cache}->get(name => 'response');
if (!defined($self->{datas})) { if (!defined($self->{datas})) {
$self->{output}->add_option_msg(short_msg => 'Cache file missing'); $self->{output}->add_option_msg(short_msg => 'Cache file missing or could not load ' . $cache_filename);
$self->{output}->option_exit(); $self->{output}->option_exit();
} }
@ -261,7 +262,9 @@ sub call_datas {
my ($self, %options) = @_; my ($self, %options) = @_;
$self->get_organizations(); $self->get_organizations();
if (!defined($options{skipNetworks})) {
$self->get_networks(orgs => [keys %{$self->{datas}->{orgs}}]); $self->get_networks(orgs => [keys %{$self->{datas}->{orgs}}]);
}
if (!defined($options{skipDevices})) { if (!defined($options{skipDevices})) {
$self->get_devices(orgs => [keys %{$self->{datas}->{orgs}}]); $self->get_devices(orgs => [keys %{$self->{datas}->{orgs}}]);
@ -269,6 +272,9 @@ sub call_datas {
if (!defined($options{skipDevicesStatus})) { if (!defined($options{skipDevicesStatus})) {
$self->get_organization_device_statuses(orgs => [keys %{$self->{datas}->{orgs}}]); $self->get_organization_device_statuses(orgs => [keys %{$self->{datas}->{orgs}}]);
} }
if (!defined($options{skipVpnTunnelsStatus})) {
$self->get_organization_vpn_tunnels_statuses(orgs => [keys %{$self->{datas}->{orgs}}]);
}
if (defined($options{cache})) { if (defined($options{cache})) {
foreach my $orgId (keys %{$self->{datas}->{orgs}}) { foreach my $orgId (keys %{$self->{datas}->{orgs}}) {
@ -408,6 +414,26 @@ sub get_organization_device_statuses {
return $self->{datas}->{devices_status}; return $self->{datas}->{devices_status};
} }
sub get_organization_vpn_tunnels_statuses {
my ($self, %options) = @_;
$self->{datas}->{vpn_tunnels_status} = {};
foreach my $id (@{$options{orgs}}) {
my $datas = $self->request_api(
endpoint => '/organizations/' . $id . '/appliance/vpn/statuses',
paginate => 300,
hostname => $self->get_shard_hostname(organization_id => $id),
ignore_codes => { 400 => 1 } # it can be disabled
);
foreach (@$datas) {
$self->{datas}->{vpn_tunnels_status}->{ $_->{deviceSerial} } = $_;
$self->{datas}->{vpn_tunnels_status}->{ $_->{deviceSerial} }->{organizationId} = $id;
}
}
return $self->{datas}->{vpn_tunnels_status};
}
sub get_network_device_uplink { sub get_network_device_uplink {
my ($self, %options) = @_; my ($self, %options) = @_;
@ -444,7 +470,7 @@ sub get_organization_uplink_loss_and_latency {
if (defined($datas)) { if (defined($datas)) {
foreach (@$datas) { foreach (@$datas) {
# sometimes uplink is undef. so we skip # sometimes uplink is undef. so we skip
next if (!defined($_->{uplink})); next if (!defined($_->{uplink}) || !defined($_->{serial}));
$self->{datas}->{uplinks_loss_latency}->{ $options{orgId} }->{ $_->{serial} } = {} $self->{datas}->{uplinks_loss_latency}->{ $options{orgId} }->{ $_->{serial} } = {}
if (!defined($self->{datas}->{uplinks_loss_latency}->{ $options{orgId} }->{ $_->{serial} })); if (!defined($self->{datas}->{uplinks_loss_latency}->{ $options{orgId} }->{ $_->{serial} }));
@ -554,7 +580,7 @@ Meraki REST API
=head1 SYNOPSIS =head1 SYNOPSIS
api_token Rest API custom mode Rest API custom mode
=head1 REST API OPTIONS =head1 REST API OPTIONS
@ -562,23 +588,23 @@ api_token Rest API custom mode
=item B<--hostname> =item B<--hostname>
Meraki api hostname (default: 'api.meraki.com') Meraki API hostname (default: 'api.meraki.com')
=item B<--port> =item B<--port>
Port used (default: 443) Define the TCP port to use to reach the API (default: 443).
=item B<--proto> =item B<--proto>
Specify https if needed (default: 'https') Define the protocol to reach the API (default: 'https').
=item B<--api-token> =item B<--api-token>
Meraki api token. Meraki API token.
=item B<--timeout> =item B<--timeout>
Set HTTP timeout Define the timeout for HTTP requests.
=item B<--ignore-permission-errors> =item B<--ignore-permission-errors>
@ -586,15 +612,15 @@ Ignore permission errors (403 status code).
=item B<--ignore-orgs-api-disabled> =item B<--ignore-orgs-api-disabled>
Ignore organizations with api disabled. Ignore organizations where the API is disabled.
=item B<--api-filter-orgs> =item B<--api-filter-orgs>
Filter organizations (regexp). Define the organizations to monitor (regular expression).
=item B<--cache-use> =item B<--cache-use>
Use the cache file (created with cache mode). Use the cache file instead of requesting the API (the cache file can be created with the cache mode).
=back =back

View File

@ -312,6 +312,7 @@ sub new {
'filter-tags:s' => { name => 'filter_tags' }, 'filter-tags:s' => { name => 'filter_tags' },
'add-switch-ports' => { name => 'add_switch_ports' }, 'add-switch-ports' => { name => 'add_switch_ports' },
'filter-switch-port:s' => { name => 'filter_switch_port' }, 'filter-switch-port:s' => { name => 'filter_switch_port' },
'filter-link-name:s' => { name => 'filter_link_name' },
'skip-traffic-disconnect-port' => { name => 'skip_traffic_disconnect_port' }, 'skip-traffic-disconnect-port' => { name => 'skip_traffic_disconnect_port' },
'skip-clients' => { name => 'skip_clients' }, 'skip-clients' => { name => 'skip_clients' },
'skip-performance' => { name => 'skip_performance' }, 'skip-performance' => { name => 'skip_performance' },
@ -373,6 +374,10 @@ sub add_uplink {
foreach (@$links) { foreach (@$links) {
my $interface = lc($_->{interface}); my $interface = lc($_->{interface});
$interface =~ s/\s+//g; $interface =~ s/\s+//g;
next if (defined($self->{option_results}->{filter_link_name}) && $self->{option_results}->{filter_link_name} ne '' &&
$interface !~ /$self->{option_results}->{filter_link_name}/);
$self->{devices}->{ $options{serial} }->{device_links}->{$interface} = { $self->{devices}->{ $options{serial} }->{device_links}->{$interface} = {
display => $interface, display => $interface,
link_status => lc($_->{status}) link_status => lc($_->{status})
@ -396,7 +401,10 @@ sub add_uplink_loss_latency {
foreach (values %$links) { foreach (values %$links) {
my $interface = lc($_->{uplink}); my $interface = lc($_->{uplink});
$interface =~ s/\s+//g; $interface =~ s/\s+//g;
next if (!defined($self->{devices}->{ $options{serial} }->{device_links}->{$interface})); next if (!defined($self->{devices}->{ $options{serial} }->{device_links}->{$interface}));
next if (defined($self->{option_results}->{filter_link_name}) && $self->{option_results}->{filter_link_name} ne '' &&
$interface !~ /$self->{option_results}->{filter_link_name}/);
my ($latency, $loss, $count) = (0, 0, 0); my ($latency, $loss, $count) = (0, 0, 0);
foreach my $ts (@{$_->{timeSeries}}) { foreach my $ts (@{$_->{timeSeries}}) {
@ -581,6 +589,10 @@ Check devices.
Filter devices by name (can be a regexp). Filter devices by name (can be a regexp).
=item B<--filter-link-name>
Filter VPN links by name (can be a regexp).
=item B<--filter-network-id> =item B<--filter-network-id>
Filter devices by network ID (can be a regexp). Filter devices by network ID (can be a regexp).
@ -611,7 +623,7 @@ Don't monitor clients traffic on device.
=item B<--skip-performance> =item B<--skip-performance>
Don't monitor appliance perfscore. Don't monitor appliance performance score.
=item B<--skip-connections> =item B<--skip-connections>

View File

@ -0,0 +1,149 @@
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package network::cisco::meraki::cloudcontroller::restapi::mode::listvpntunnels;
use base qw(centreon::plugins::mode);
use strict;
use warnings;
my @labels = (
'organization_id',
'organization_name',
'network_id',
'network_name',
'device_serial',
'mode',
'status'
);
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options);
bless $self, $class;
$options{options}->add_options(arguments => {
'filter-network-id:s' => { name => 'filter_network_id' },
'filter-organization-name:s' => { name => 'filter_organization_name' },
'filter-organization-id:s' => { name => 'filter_organization_id' }
});
return $self;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::init(%options);
}
sub manage_selection {
my ($self, %options) = @_;
my $organizations = $options{custom}->get_organizations();
my $devices = $options{custom}->get_organization_vpn_tunnels_statuses(
orgs => [keys %$organizations]
);
my $results = {};
foreach (keys %$devices) {
next if (defined($self->{option_results}->{filter_network_id}) && $self->{option_results}->{filter_network_id} ne '' &&
$devices->{$_}->{networkId} !~ /$self->{option_results}->{filter_network_id}/);
next if (defined($self->{option_results}->{filter_organization_id}) && $self->{option_results}->{filter_organization_id} ne '' &&
$devices->{$_}->{organizationId} !~ /$self->{option_results}->{filter_organization_id}/);
my $organization_name = $organizations->{ $devices->{$_}->{organizationId} }->{name};
next if (defined($self->{option_results}->{filter_organization_name}) && $self->{option_results}->{filter_organization_name} ne '' &&
$organization_name !~ /$self->{option_results}->{filter_organization_name}/);
$results->{$_} = {
network_id => $devices->{$_}->{networkId},
network_name => $devices->{$_}->{networkName},
device_serial => $devices->{$_}->{deviceSerial},
organization_id => $devices->{$_}->{organizationId},
organization_name => $organization_name,
mode => $devices->{$_}->{vpnMode},
status => $devices->{$_}->{deviceStatus}
};
}
return $results;
}
sub run {
my ($self, %options) = @_;
my $results = $self->manage_selection(custom => $options{custom});
foreach my $instance (sort keys %$results) {
$self->{output}->output_add(long_msg =>
join('', map("[$_: " . $results->{$instance}->{$_} . ']', @labels))
);
}
$self->{output}->output_add(
severity => 'OK',
short_msg => 'List VPN tunnels:'
);
$self->{output}->display(nolabel => 1, force_ignore_perfdata => 1, force_long_output => 1);
$self->{output}->exit();
}
sub disco_format {
my ($self, %options) = @_;
$self->{output}->add_disco_format(elements => [@labels]);
}
sub disco_show {
my ($self, %options) = @_;
my $results = $self->manage_selection(custom => $options{custom});
foreach (sort keys %$results) {
$self->{output}->add_disco_entry(
%{$results->{$_}}
);
}
}
1;
__END__
=head1 MODE
List VPN tunnels.
=over 8
=item B<--filter-network-id>
Filter VPN tunnels by network ID (can be a regexp).
=item B<--filter-organization-id>
Filter VPN tunnels by organization ID (can be a regexp).
=item B<--filter-organization-name>
Filter VPN tunnels by organization name (can be a regexp).
=back
=cut

View File

@ -126,7 +126,7 @@ sub manage_selection {
(defined($self->{option_results}->{filter_organization_name}) ? $self->{option_results}->{filter_organization_name} : 'all') (defined($self->{option_results}->{filter_organization_name}) ? $self->{option_results}->{filter_organization_name} : 'all')
); );
my $datas = $options{custom}->get_datas(skipDevices => 1, skipDevicesStatus => 1); my $datas = $options{custom}->get_datas(skipDevices => 1, skipDevicesStatus => 1, skipVpnTunnelsStatus => 1);
$self->{networks} = {}; $self->{networks} = {};
foreach my $id (keys %{$datas->{networks}}) { foreach my $id (keys %{$datas->{networks}}) {

View File

@ -0,0 +1,188 @@
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package network::cisco::meraki::cloudcontroller::restapi::mode::vpntunnels;
use base qw(centreon::plugins::templates::counter);
use strict;
use warnings;
use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng);
use Digest::MD5 qw(md5_hex);
sub custom_status_output {
my ($self, %options) = @_;
return 'status: ' . $self->{result_values}->{status} . ' [mode: ' . $self->{result_values}->{mode} . ']';
}
sub prefix_tunnel_output {
my ($self, %options) = @_;
return "vpn tunnel '" . $options{instance_value}->{deviceSerial} . "' ";
}
sub prefix_global_output {
my ($self, %options) = @_;
return 'Vpn tunnels ';
}
sub set_counters {
my ($self, %options) = @_;
$self->{maps_counters_type} = [
{ name => 'global', type => 0, cb_prefix_output => 'prefix_global_output', skipped_code => { -10 => 1 } },
{ name => 'tunnels', type => 1, cb_prefix_output => 'prefix_tunnel_output', message_multiple => 'All vpn tunnels are ok' }
];
$self->{maps_counters}->{global} = [
{ label => 'total-online', nlabel => 'vpn.tunnels.online.count', display_ok => 0, set => {
key_values => [ { name => 'online' }, { name => 'total' } ],
output_template => 'online: %s',
perfdatas => [
{ template => '%s', min => 0, max => 'total' }
]
}
},
{ label => 'total-offline', nlabel => 'vpn.tunnels.offline.count', display_ok => 0, set => {
key_values => [ { name => 'offline' }, { name => 'total' } ],
output_template => 'offline: %s',
perfdatas => [
{ template => '%s', min => 0, max => 'total' }
]
}
},
{ label => 'total-dormant', nlabel => 'vpn.tunnels.dormant.count', display_ok => 0, set => {
key_values => [ { name => 'dormant' }, { name => 'total' } ],
output_template => 'dormant: %s',
perfdatas => [
{ template => '%s', min => 0, max => 'total' }
]
}
}
];
$self->{maps_counters}->{tunnels} = [
{
label => 'status', type => 2,
critical_default => '%{status} =~ /offline/i',
set => {
key_values => [ { name => 'status' }, { name => 'mode' }, { name => 'deviceSerial' } ],
closure_custom_output => $self->can('custom_status_output'),
closure_custom_perfdata => sub { return 0; },
closure_custom_threshold_check => \&catalog_status_threshold_ng
}
}
];
}
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1);
bless $self, $class;
$options{options}->add_options(arguments => {
'filter-network-name:s' => { name => 'filter_network_name' },
'filter-organization-name:s' => { name => 'filter_organization_name' },
'filter-organization-id:s' => { name => 'filter_organization_id' },
'filter-device-serial:s' => { name => 'filter_device_serial' }
});
return $self;
}
sub manage_selection {
my ($self, %options) = @_;
my $datas = $options{custom}->get_datas(skipDevices => 1, skipDevicesStatus => 1, skipNetworks => 1);
$self->{global} = { total => 0, online => 0, offline => 0, dormant => 0 };
$self->{tunnels} = {};
foreach my $id (keys %{$datas->{vpn_tunnels_status}}) {
next if (defined($self->{option_results}->{filter_network_name}) && $self->{option_results}->{filter_network_name} ne '' &&
$datas->{vpn_tunnels_status}->{$id}->{networkName} !~ /$self->{option_results}->{filter_network_name}/);
next if (defined($self->{option_results}->{filter_organization_id}) && $self->{option_results}->{filter_organization_id} ne '' &&
$datas->{vpn_tunnels_status}->{$id}->{organizationId} !~ /$self->{option_results}->{filter_organization_id}/);
next if (defined($self->{option_results}->{filter_organization_name}) && $self->{option_results}->{filter_organization_name} ne '' &&
$datas->{orgs}->{ $datas->{vpn_tunnels_status}->{$id}->{organizationId} }->{name} !~ /$self->{option_results}->{filter_organization_name}/);
$self->{tunnels}->{$id} = {
deviceSerial => $id,
status => $datas->{vpn_tunnels_status}->{$id}->{deviceStatus},
mode => $datas->{vpn_tunnels_status}->{$id}->{vpnMode}
};
$self->{global}->{total}++;
$self->{global}->{ lc($datas->{vpn_tunnels_status}->{$id}->{deviceStatus}) }++
if (defined($self->{global}->{ lc($datas->{vpn_tunnels_status}->{$id}->{deviceStatus}) }));
}
}
1;
__END__
=head1 MODE
Check VPN tunnels.
=over 8
=item B<--filter-network-name>
Filter VPN tunnels by network name (can be a regexp).
=item B<--filter-organization-id>
Filter VPN tunnels by organization ID (can be a regexp).
=item B<--filter-organization-name>
Filter VPN tunnels by organization name (can be a regexp).
=item B<--filter-device-serial>
Filter VPN tunnels by device serial (can be a regexp).
=item B<--unknown-status>
Define the conditions to match for the status to be UNKNOWN.
You can use the following variables: %{status}, %{deviceSerial}, %{mode}
=item B<--warning-status>
Define the conditions to match for the status to be WARNING.
You can use the following variables: %{status}, %{deviceSerial}, %{mode}
=item B<--critical-status>
Define the conditions to match for the status to be CRITICAL (default: '%{status} =~ /offline/i').
You can use the following variables: %{status}, %{deviceSerial}, %{mode}
=item B<--warning-*> B<--critical-*>
Thresholds.
Can be: 'total-online', 'total-offline', 'total-dormant'.
=back
=cut

View File

@ -36,7 +36,9 @@ sub new {
'discovery' => 'network::cisco::meraki::cloudcontroller::restapi::mode::discovery', 'discovery' => 'network::cisco::meraki::cloudcontroller::restapi::mode::discovery',
'list-devices' => 'network::cisco::meraki::cloudcontroller::restapi::mode::listdevices', 'list-devices' => 'network::cisco::meraki::cloudcontroller::restapi::mode::listdevices',
'list-tags' => 'network::cisco::meraki::cloudcontroller::restapi::mode::listtags', 'list-tags' => 'network::cisco::meraki::cloudcontroller::restapi::mode::listtags',
'networks' => 'network::cisco::meraki::cloudcontroller::restapi::mode::networks' 'list-vpn-tunnels' => 'network::cisco::meraki::cloudcontroller::restapi::mode::listvpntunnels',
'networks' => 'network::cisco::meraki::cloudcontroller::restapi::mode::networks',
'vpn-tunnels' => 'network::cisco::meraki::cloudcontroller::restapi::mode::vpntunnels'
}; };
$self->{custom_modes}->{api} = 'network::cisco::meraki::cloudcontroller::restapi::custom::api'; $self->{custom_modes}->{api} = 'network::cisco::meraki::cloudcontroller::restapi::custom::api';

View File

@ -1,5 +1,7 @@
--add-sysdesc --add-sysdesc
--api-filter-orgs
--api-password --api-password
--api-token
--api-version --api-version
--cacert-file --cacert-file
--cert-pkcs12 --cert-pkcs12
@ -15,6 +17,7 @@
--force-counters64 --force-counters64
--force-oid --force-oid
--get-param --get-param
--ignore-orgs-api-disabled
--lookup-perfdatas-nagios --lookup-perfdatas-nagios
--map-speed-dsl --map-speed-dsl
--mqtt --mqtt
@ -47,6 +50,7 @@ Iwsva
Loggly Loggly
MBean MBean
MQTT MQTT
Meraki
Mosquitto Mosquitto
NTLMv2 NTLMv2
NagVis NagVis
@ -59,11 +63,16 @@ SNMP
SSH SSH
Sansymphony Sansymphony
SureBackup SureBackup
TCP
TendMicro TendMicro
VDSL2 VDSL2
VPN
Veeam Veeam
WSMAN WSMAN
XPath XPath
api.meraki.com
connections-dhcp
connections-dns
deltaps deltaps
df df
eth eth
@ -93,6 +102,8 @@ space-usage-prct
teampass teampass
timeframe timeframe
topic-messages-inflighted topic-messages-inflighted
total-offline-prct
total-online-prct
total-oper-down total-oper-down
total-oper-up total-oper-up
uptime uptime

View File

@ -0,0 +1,480 @@
{
"uuid": "a1da12c3-97f5-4ab7-bec7-a30e1c36309a",
"lastMigration": 32,
"name": "Meraki.mockoon",
"endpointPrefix": "",
"latency": 0,
"port": 3000,
"hostname": "",
"folders": [],
"routes": [
{
"uuid": "77da22e2-4161-4be7-a598-62e372cf0438",
"type": "http",
"documentation": "",
"method": "get",
"endpoint": "api/v1/organizations",
"responses": [
{
"uuid": "4c2ab123-f20d-4e65-a422-096ecec496ce",
"body": "",
"latency": 0,
"statusCode": 200,
"label": "",
"headers": [
{
"key": "access-control-allow-headers",
"value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With"
},
{
"key": "access-control-allow-methods",
"value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS"
},
{
"key": "access-control-allow-origin",
"value": "*"
},
{
"key": "content-security-policy",
"value": "default-src 'none'"
},
{
"key": "content-type",
"value": "text/html; charset=utf-8"
},
{
"key": "x-content-type-options",
"value": "nosniff"
}
],
"bodyType": "DATABUCKET",
"filePath": "",
"databucketID": "xl8l",
"sendFileAsBody": false,
"rules": [],
"rulesOperator": "OR",
"disableTemplating": false,
"fallbackTo404": false,
"default": false,
"crudKey": "id",
"callbacks": []
}
],
"responseMode": null
},
{
"uuid": "3d572355-f7f4-4558-abc6-0e4c2c8b06f9",
"type": "http",
"documentation": "",
"method": "get",
"endpoint": "api/v1/organizations/123456789123456789/appliance/vpn/statuses",
"responses": [
{
"uuid": "4baedfc4-2a1d-4dd1-aff6-e07f6c4144e9",
"latency": 0,
"statusCode": 200,
"label": "",
"headers": [
{
"key": "access-control-allow-headers",
"value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With"
},
{
"key": "access-control-allow-methods",
"value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS"
},
{
"key": "access-control-allow-origin",
"value": "*"
},
{
"key": "content-security-policy",
"value": "default-src 'none'"
},
{
"key": "content-type",
"value": "text/html; charset=utf-8"
},
{
"key": "x-content-type-options",
"value": "nosniff"
}
],
"bodyType": "DATABUCKET",
"filePath": "",
"databucketID": "zqoq",
"sendFileAsBody": false,
"rules": [],
"rulesOperator": "OR",
"disableTemplating": false,
"fallbackTo404": false,
"default": false,
"crudKey": "id",
"callbacks": [],
"body": "{}"
}
],
"responseMode": null
},
{
"uuid": "c8872c69-c290-4c5e-aa4b-18f8ffaf084b",
"type": "http",
"documentation": "",
"method": "get",
"endpoint": "api/v1/organizations/123456789123456789/networks",
"responses": [
{
"uuid": "0723def4-07b5-4549-962a-e041423a9733",
"body": "[]",
"latency": 0,
"statusCode": 200,
"label": "",
"headers": [
{
"key": "access-control-allow-headers",
"value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With"
},
{
"key": "access-control-allow-methods",
"value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS"
},
{
"key": "access-control-allow-origin",
"value": "*"
},
{
"key": "content-security-policy",
"value": "default-src 'none'"
},
{
"key": "content-type",
"value": "text/html; charset=utf-8"
},
{
"key": "x-content-type-options",
"value": "nosniff"
}
],
"bodyType": "INLINE",
"filePath": "",
"databucketID": "",
"sendFileAsBody": false,
"rules": [],
"rulesOperator": "OR",
"disableTemplating": false,
"fallbackTo404": false,
"default": false,
"crudKey": "id",
"callbacks": []
}
],
"responseMode": null
},
{
"uuid": "e7b443c0-4b6e-41b0-bfad-6b23fd14a9cd",
"type": "http",
"documentation": "",
"method": "get",
"endpoint": "api/v1/organizations/123456789123456789/devices",
"responses": [
{
"uuid": "c1d9369d-ee0e-4b4c-b6df-5f64f4112382",
"body": "[]",
"latency": 0,
"statusCode": 200,
"label": "",
"headers": [
{
"key": "access-control-allow-headers",
"value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With"
},
{
"key": "access-control-allow-methods",
"value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS"
},
{
"key": "access-control-allow-origin",
"value": "*"
},
{
"key": "content-security-policy",
"value": "default-src 'none'"
},
{
"key": "content-type",
"value": "text/html; charset=utf-8"
},
{
"key": "x-content-type-options",
"value": "nosniff"
}
],
"bodyType": "INLINE",
"filePath": "",
"databucketID": "",
"sendFileAsBody": false,
"rules": [],
"rulesOperator": "OR",
"disableTemplating": false,
"fallbackTo404": false,
"default": false,
"crudKey": "id",
"callbacks": []
}
],
"responseMode": null
},
{
"uuid": "83ae251f-e2ac-4eb0-bed3-9d024d2d4055",
"type": "http",
"documentation": "",
"method": "get",
"endpoint": "api/v1/organizations/123456789123456789/devices/statuses",
"responses": [
{
"uuid": "e459d826-8630-498d-983f-5259052584b7",
"body": "[]",
"latency": 0,
"statusCode": 200,
"label": "",
"headers": [
{
"key": "access-control-allow-headers",
"value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With"
},
{
"key": "access-control-allow-methods",
"value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS"
},
{
"key": "access-control-allow-origin",
"value": "*"
},
{
"key": "content-security-policy",
"value": "default-src 'none'"
},
{
"key": "content-type",
"value": "text/html; charset=utf-8"
},
{
"key": "x-content-type-options",
"value": "nosniff"
}
],
"bodyType": "INLINE",
"filePath": "",
"databucketID": "",
"sendFileAsBody": false,
"rules": [],
"rulesOperator": "OR",
"disableTemplating": false,
"fallbackTo404": false,
"default": false,
"crudKey": "id",
"callbacks": []
}
],
"responseMode": null
},
{
"uuid": "05351271-5695-4ccc-bdd2-75fd0ab68def",
"type": "http",
"documentation": "",
"method": "get",
"endpoint": "api/v1/organizations/123456789123456789/uplinks/statuses",
"responses": [
{
"uuid": "e6e461fe-86d2-43c1-adb9-73047593e783",
"body": "[]",
"latency": 0,
"statusCode": 200,
"label": "",
"headers": [
{
"key": "access-control-allow-headers",
"value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With"
},
{
"key": "access-control-allow-methods",
"value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS"
},
{
"key": "access-control-allow-origin",
"value": "*"
},
{
"key": "content-security-policy",
"value": "default-src 'none'"
},
{
"key": "content-type",
"value": "text/html; charset=utf-8"
},
{
"key": "x-content-type-options",
"value": "nosniff"
}
],
"bodyType": "INLINE",
"filePath": "",
"databucketID": "",
"sendFileAsBody": false,
"rules": [],
"rulesOperator": "OR",
"disableTemplating": false,
"fallbackTo404": false,
"default": false,
"crudKey": "id",
"callbacks": []
}
],
"responseMode": null
},
{
"uuid": "1ee10c37-cb78-4594-9fd9-006a3927f6f2",
"type": "http",
"documentation": "",
"method": "get",
"endpoint": "api/v1/organizations/123456789123456789/devices/uplinksLossAndLatency",
"responses": [
{
"uuid": "18e3e93c-302d-4fda-86d1-9ffd1a8b8abd",
"body": "[]",
"latency": 0,
"statusCode": 200,
"label": "",
"headers": [
{
"key": "access-control-allow-headers",
"value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With"
},
{
"key": "access-control-allow-methods",
"value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS"
},
{
"key": "access-control-allow-origin",
"value": "*"
},
{
"key": "content-security-policy",
"value": "default-src 'none'"
},
{
"key": "content-type",
"value": "text/html; charset=utf-8"
},
{
"key": "x-content-type-options",
"value": "nosniff"
}
],
"bodyType": "INLINE",
"filePath": "",
"databucketID": "",
"sendFileAsBody": false,
"rules": [],
"rulesOperator": "OR",
"disableTemplating": false,
"fallbackTo404": false,
"default": false,
"crudKey": "id",
"callbacks": []
}
],
"responseMode": null
}
],
"rootChildren": [
{
"type": "route",
"uuid": "77da22e2-4161-4be7-a598-62e372cf0438"
},
{
"type": "route",
"uuid": "3d572355-f7f4-4558-abc6-0e4c2c8b06f9"
},
{
"type": "route",
"uuid": "c8872c69-c290-4c5e-aa4b-18f8ffaf084b"
},
{
"type": "route",
"uuid": "e7b443c0-4b6e-41b0-bfad-6b23fd14a9cd"
},
{
"type": "route",
"uuid": "83ae251f-e2ac-4eb0-bed3-9d024d2d4055"
},
{
"type": "route",
"uuid": "05351271-5695-4ccc-bdd2-75fd0ab68def"
},
{
"type": "route",
"uuid": "1ee10c37-cb78-4594-9fd9-006a3927f6f2"
}
],
"proxyMode": false,
"proxyHost": "",
"proxyRemovePrefix": false,
"tlsOptions": {
"enabled": false,
"type": "CERT",
"pfxPath": "",
"certPath": "",
"keyPath": "",
"caPath": "",
"passphrase": ""
},
"cors": true,
"headers": [
{
"key": "Content-Type",
"value": "application/json"
},
{
"key": "Access-Control-Allow-Origin",
"value": "*"
},
{
"key": "Access-Control-Allow-Methods",
"value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS"
},
{
"key": "Access-Control-Allow-Headers",
"value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With"
}
],
"proxyReqHeaders": [
{
"key": "",
"value": ""
}
],
"proxyResHeaders": [
{
"key": "",
"value": ""
}
],
"data": [
{
"uuid": "80e9ddfd-1272-45d6-a29e-c927f0f8cebd",
"id": "xl8l",
"name": "Organizations",
"documentation": "",
"value": "[\n {\n \"id\": \"123456789123456789\",\n \"name\": \"Endor\",\n \"url\": \"http://127.0.0.1:3000/o/NQkU0cWc/manage/organization/overview\",\n \"samlConsumerUrl\": \"http://127.0.0.1:3000/saml/login/NQkU0cWc/RV3RrcB2UCVa\",\n \"samlConsumerUrls\": [\n \"http://127.0.0.1:3000/saml/login/NQkU0cWc/RV3RrcB2UCVa\"\n ],\n \"api\": {\n \"enabled\": true\n },\n \"licensing\": {\n \"model\": \"per-device\"\n },\n \"cloud\": {\n \"region\": {\n \"name\": \"Europe\",\n \"host\": {\n \"name\": \"Europe\"\n }\n }\n },\n \"management\": {\n \"details\": [\n {\n \"name\": \"customer number\",\n \"value\": \"56417983\"\n }\n ]\n }\n }\n]\n"
},
{
"uuid": "337987b2-984c-4882-9e6e-83a2c2b714f5",
"id": "zqoq",
"name": "VPN Tunnels",
"documentation": "",
"value": "[\n {\n \"networkId\": \"Z_000000000000000001\",\n \"networkName\": \"ALDERAAN\",\n \"deviceSerial\": \"C3PO-R2P2-BB88\",\n \"deviceStatus\": \"dormant\",\n \"uplinks\": [],\n \"vpnMode\": \"spoke\",\n \"exportedSubnets\": [\n {\n \"name\": \"Single LAN Settings\",\n \"subnet\": \"172.16.254.24/29\"\n }\n ],\n \"merakiVpnPeers\": [\n {\n \"networkId\": \"Z_000000000000000002\",\n \"networkName\": \"KASHYYYK\",\n \"reachability\": \"unreachable\"\n },\n {\n \"networkId\": \"Z_000000000000000003\",\n \"networkName\": \"TATOOINE\",\n \"reachability\": \"unreachable\"\n },\n {\n \"networkId\": \"Z_000000000000000004\",\n \"networkName\": \"HOTH\",\n \"reachability\": \"unreachable\"\n }\n ],\n \"thirdPartyVpnPeers\": []\n }\n]"
}
],
"callbacks": []
}

View File

@ -0,0 +1,46 @@
*** Settings ***
Documentation Meraki VPN Tunnels
Resource ${CURDIR}${/}..${/}..${/}..${/}..${/}..${/}..${/}resources/import.resource
Suite Setup Start Mockoon ${MOCKOON_JSON}
Test Timeout 120s
*** Variables ***
${MOCKOON_JSON} ${CURDIR}${/}meraki.mockoon.json
${CMD} ${CENTREON_PLUGINS} --plugin=network::cisco::meraki::cloudcontroller::restapi::plugin
... --api-token=EEECGFCGFCGF
... --statefile-dir=/dev/shm/
*** Test Cases ***
Create cache from API
[Tags] meraki api vpn network cache
${output} Run
... ${CMD} --mode=cache --proto http --port 3000 --hostname=127.0.0.1
${output} Strip String ${output}
Should Be Equal As Strings
... ${output}
... OK: Cache files created successfully
... Wrong output result:\n\n ${output}\nInstead of:\n OK: Cache files created successfully\n\n
# Mockoon is not needed any longer since the data are cached
Stop Mockoon
Check if ${test_desc} works
[Tags] meraki api vpn network
${output} Run
... ${CMD} --mode=vpn-tunnels --filter-network-name=${filter_network_name} --cache-use --critical-total-dormant=1:
${output} Strip String ${output}
Should Be Equal As Strings
... ${output}
... ${expected}
... Wrong output result:\n\n ${output}\nInstead of:\n ${expected}\n\n
Examples: test_desc filter_network_name expected --
... all links .* OK: vpn tunnel 'C3PO-R2P2-BB88' status: dormant [mode: spoke] | 'vpn.tunnels.online.count'=0;;;0;1 'vpn.tunnels.offline.count'=0;;;0;1 'vpn.tunnels.dormant.count'=1;;1:;0;1
... empty filter ${EMPTY} OK: vpn tunnel 'C3PO-R2P2-BB88' status: dormant [mode: spoke] | 'vpn.tunnels.online.count'=0;;;0;1 'vpn.tunnels.offline.count'=0;;;0;1 'vpn.tunnels.dormant.count'=1;;1:;0;1
... absurd filter toto CRITICAL: Vpn tunnels dormant: 0 | 'vpn.tunnels.online.count'=0;;;0;0 'vpn.tunnels.offline.count'=0;;;0;0 'vpn.tunnels.dormant.count'=0;;1:;0;0