Add Sirportly notification plugin

This commit is contained in:
UrBnW 2020-02-05 15:40:22 +01:00
parent 97f3184476
commit a688b5290f
2 changed files with 416 additions and 0 deletions

View File

@ -0,0 +1,368 @@
#
# Copyright 2020 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 notification::sirportly::mode::ticket;
use base qw(centreon::plugins::mode);
use strict;
use warnings;
use centreon::plugins::http;
use centreon::plugins::statefile;
use JSON::XS;
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options);
bless $self, $class;
$options{options}->add_options(arguments => {
'hostname:s' => { name => 'hostname', default => 'api.sirportly.com' },
'port:s' => { name => 'port', default => 443 },
'proto:s' => { name => 'proto', default => 'https' },
'api-token:s' => { name => 'api_token' },
'api-secret:s' => { name => 'api_secret' },
'cache' => { name => 'cache' },
'contact-name:s' => { name => 'contact_name', default => 'Centreon Notifications' },
'contact-type:s' => { name => 'contact_type', default => 'telephone' },
'contact-data:s' => { name => 'contact_data', default => '-' },
'brand:s' => { name => 'brand' },
'department:s' => { name => 'department' },
'submit-user:s' => { name => 'submit_user' },
'close-user:s' => { name => 'close_user' },
'submit-status:s' => { name => 'submit_status' },
'close-status:s' => { name => 'close_status' },
'priority-mapping:s' => { name => 'priority_mapping' },
'priority:s' => { name => 'priority' },
'item-id:s' => { name => 'item_id' },
'subject:s' => { name => 'subject' },
'message:s' => { name => 'message' },
'timeout:s' => { name => 'timeout' },
});
$self->{http} = centreon::plugins::http->new(%options);
$self->{cache} = centreon::plugins::statefile->new(%options);
return $self;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::init(%options);
if (!defined($self->{option_results}->{api_token})) {
$self->{output}->add_option_msg(short_msg => "You need to set --api-token option");
$self->{output}->option_exit();
}
if (!defined($self->{option_results}->{api_secret})) {
$self->{output}->add_option_msg(short_msg => "You need to set --api-secret option");
$self->{output}->option_exit();
}
if (!defined($self->{option_results}->{brand})) {
$self->{output}->add_option_msg(short_msg => "You need to set --brand option");
$self->{output}->option_exit();
}
if (!defined($self->{option_results}->{department})) {
$self->{output}->add_option_msg(short_msg => "You need to set --department option");
$self->{output}->option_exit();
}
if (!defined($self->{option_results}->{submit_status})) {
$self->{output}->add_option_msg(short_msg => "You need to set --submit-status option");
$self->{output}->option_exit();
}
if (!defined($self->{option_results}->{close_status})) {
$self->{output}->add_option_msg(short_msg => "You need to set --close-status option");
$self->{output}->option_exit();
}
if (!defined($self->{option_results}->{priority})) {
$self->{output}->add_option_msg(short_msg => "You need to set --priority option");
$self->{output}->option_exit();
}
if (!defined($self->{option_results}->{item_id})) {
$self->{output}->add_option_msg(short_msg => "You need to set --item-id option");
$self->{output}->option_exit();
}
if (!defined($self->{option_results}->{subject})) {
$self->{output}->add_option_msg(short_msg => "You need to set --subject option");
$self->{output}->option_exit();
}
if (!defined($self->{option_results}->{message})) {
$self->{output}->add_option_msg(short_msg => "You need to set --message option");
$self->{output}->option_exit();
}
$self->{http}->set_options(%{$self->{option_results}});
$self->{cache}->check_options(option_results => $self->{option_results});
}
sub proceed {
my ($self, %options) = @_;
# Let's compute a priority mapping hash, with lc keys
my %priority_mapping;
if (defined($self->{option_results}->{priority_mapping})) {
my $i = 0;
%priority_mapping = map { $i++ % 2 ? $_ : lc } split /[:,]/, $self->{option_results}->{priority_mapping};
}
# Let's compute a ticket tag to be able to find the opened ticket later on
my $tag = $options{item_id};
if ($options{priority} =~ /^(UP|DOWN|UNREACHABLE)$/i) {
$tag = "centreon-hst:$tag";
} else {
$tag = "centreon-svc:$tag";
}
# Is there an existing Sirportly ticket for this item ?
my $post_param = [
"spql=SELECT tickets.reference,statuses.name FROM tickets WHERE ticket_tags.tag=\"$tag\" AND statuses.name!=\"$self->{option_results}->{close_status}\" ORDER BY tickets.submitted_at DESC LIMIT 1",
];
my $response = $self->{http}->request(method => 'POST', url_path =>'/api/v2/tickets/spql', post_param => $post_param, warning_status => '', unknown_status => '', critical_status => '');
my $ticket_info;
eval {
$ticket_info = JSON::XS->new->decode($response)->{results}[0];
};
# Close the ticket
if ($options{priority} =~ /^(UP|OK)$/i) {
if (defined($ticket_info)) {
$post_param = [
"ticket=$ticket_info->[0]",
"contact=$self->{option_results}->{contact_name}",
"message=$options{message}",
];
$self->{http}->request(method => 'POST', url_path =>'/api/v2/tickets/post_update', post_param => $post_param, warning_status => '', unknown_status => '', critical_status => '');
if ($ticket_info->[1] eq $self->{option_results}->{submit_status}) {
$post_param = [
"ticket=$ticket_info->[0]",
"user=" . (defined($self->{option_results}->{close_user}) ? $self->{option_results}->{close_user} : ""),
"status=$self->{option_results}->{close_status}",
];
my $decoded = $self->{http}->request(method => 'POST', url_path =>'/api/v2/tickets/update', post_param => $post_param);
eval {
$decoded = JSON::XS->new->decode($response);
};
if ($@) {
$ticket_info->[0] = "failed";
}
}
}
}
# Update an existing ticket
elsif (defined($ticket_info)) {
$post_param = [
"ticket=$ticket_info->[0]",
"contact=$self->{option_results}->{contact_name}",
"message=$options{message}",
];
$self->{http}->request(method => 'POST', url_path =>'/api/v2/tickets/post_update', post_param => $post_param, warning_status => '', unknown_status => '', critical_status => '');
$post_param = [
"ticket=$ticket_info->[0]",
"priority=" . (defined($priority_mapping{lc($options{priority})}) ? $priority_mapping{lc($options{priority})} : $options{priority}),
];
my $decoded = $self->{http}->request(method => 'POST', url_path =>'/api/v2/tickets/update', post_param => $post_param);
eval {
$decoded = JSON::XS->new->decode($response);
};
if ($@) {
$ticket_info->[0] = "failed";
}
}
# Open a new ticket
else {
$post_param = [
"contact_name=$self->{option_results}->{contact_name}",
"contact_method_type=$self->{option_results}->{contact_type}",
"contact_method_data=$self->{option_results}->{contact_data}",
"brand=$self->{option_results}->{brand}",
"department=$self->{option_results}->{department}",
"user=" . (defined($self->{option_results}->{submit_user}) ? $self->{option_results}->{submit_user} : ""),
"status=$self->{option_results}->{submit_status}",
"priority=" . (defined($priority_mapping{lc($options{priority})}) ? $priority_mapping{lc($options{priority})} : $options{priority}),
"tag_list=$tag",
"subject=$options{subject}",
"message=$options{message}",
];
my $response = $self->{http}->request(method => 'POST', url_path =>'/api/v2/tickets/submit', post_param => $post_param, warning_status => '', unknown_status => '', critical_status => '');
my $decoded;
eval {
$decoded = JSON::XS->new->decode($response);
};
if ($@ || !defined($decoded->{reference})) {
$ticket_info->[0] = "failed";
} else {
$ticket_info->[0] = $decoded->{reference};
}
}
# Return proceeded ticket
return $ticket_info->[0];
}
sub run {
my ($self, %options) = @_;
# Authentication headers
$self->{http}->add_header(key => 'X-Auth-Token', value => $self->{option_results}->{api_token});
$self->{http}->add_header(key => 'X-Auth-Secret', value => $self->{option_results}->{api_secret});
# Load cache
$self->{cache}->read(statefile => 'sirportly_api_' . $self->{option_results}->{api_token});
my $cache;
if (defined($self->{option_results}->{cache})) {
$cache = $self->{cache}->get(name => 'cache');
} else {
$cache = {};
}
# Add current item to cache
my $cache_index = time();
$cache->{$cache_index}->{item_id} = $self->{option_results}->{item_id};
$cache->{$cache_index}->{priority} = $self->{option_results}->{priority};
$cache->{$cache_index}->{subject} = $self->{option_results}->{subject};
$cache->{$cache_index}->{message} = $self->{option_results}->{message};
$self->{cache}->write(data => {cache => $cache});
# Proceed cached notifications
my $cache_size = keys %{$cache};
my $cache_done = 0;
foreach $cache_index (sort keys %{$cache}) {
my $ticket_reference = $self->proceed(
priority => $cache->{$cache_index}->{priority},
item_id => $cache->{$cache_index}->{item_id},
subject => $cache->{$cache_index}->{subject},
message => $cache->{$cache_index}->{message},
);
if (!defined($ticket_reference) || $ticket_reference ne "failed") {
delete($cache->{$cache_index});
$self->{cache}->write(data => {cache => $cache});
$cache_done++;
} else {
last;
}
}
# Exit
$self->{output}->output_add(severity => ($cache_size == $cache_done) ? 'OK' : 'CRITICAL',
short_msg => "Proceeded " . $cache_done . "/" . $cache_size . " notifications");
$self->{output}->display(force_ignore_perfdata => 1);
$self->{output}->exit();
}
1;
__END__
=head1 MODE
Open tickets via Sirportly API (https://sirportly.com/docs/api-specification/)
=over 6
=item B<--hostname>
Hostname of the OVH SMS API (default: 'api.sirportly.com').
=item B<--port>
Port used by API (default: '443').
=item B<--proto>
Specify https if needed (Default: 'https').
=item B<--api-token>
Specify the API authentication token.
=item B<--api-secret>
Specify the API authentication secret.
=item B<--cache>
Cache notifications not correctly delivered, to retry at next run.
=item B<--contact-name>
Specify the contact name for this ticket (default: 'Centreon Notifications').
=item B<--contact-type>
Specify the contact type (email or telephone) of the contact (default: 'telephone').
=item B<--contact-data>
Specify the email address or telephone number of the contact (default: '-').
=item B<--brand>
Specify the brand for this ticket.
=item B<--department>
Specify the department for this ticket.
=item B<--submit-user>
Specify the user to assign the ticket to when submitting the ticket.
=item B<--close-user>
Specify the user to assign the ticket to when closing the ticket.
=item B<--submit-status>
Specify the Sirportly status to be used when submitting the ticket.
=item B<--close-status>
Specify the Sirportly status to be used when closing the ticket.
=item B<--priority-mapping>
Map the Centreon priorities to Sirportly ones, comma separated (syntax: (down|unreachable|warning|critical|unknown):priority).
=item B<--priority>
Specify the priority for this ticket.
=item B<--item-id>
Specify the host or service ID for this ticket.
=item B<--subject>
Specify the subject for this ticket.
=item B<--message>
Specify the message for this ticket.
=item B<--timeout>
Threshold for HTTP timeout
=back
=cut

View File

@ -0,0 +1,48 @@
#
# Copyright 2020 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 notification::sirportly::plugin;
use strict;
use warnings;
use base qw(centreon::plugins::script_simple);
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options);
bless $self, $class;
$self->{version} = '1.0';
%{$self->{modes}} = (
'ticket' => 'notification::sirportly::mode::ticket',
);
return $self;
}
1;
__END__
=head1 PLUGIN DESCRIPTION
Open tickets via Sirportly API.
=cut