From a8d780d6ed9e0b0a873864547fe283553fe17088 Mon Sep 17 00:00:00 2001 From: garnier-quentin Date: Fri, 5 Feb 2016 13:11:33 +0100 Subject: [PATCH] + add slack to send notification --- centreon/plugins/http.pm | 13 +- notification/slack/mode/alert.pm | 343 +++++++++++++++++++++++++++++++ notification/slack/plugin.pm | 48 +++++ 3 files changed, 402 insertions(+), 2 deletions(-) create mode 100644 notification/slack/mode/alert.pm create mode 100644 notification/slack/plugin.pm diff --git a/centreon/plugins/http.pm b/centreon/plugins/http.pm index de399a190..59e5fb41b 100644 --- a/centreon/plugins/http.pm +++ b/centreon/plugins/http.pm @@ -205,7 +205,9 @@ sub request { my ($response, $content); my ($req, $url); - if (defined($request_options->{port}) && $request_options->{port} =~ /^[0-9]+$/) { + if (defined($request_options->{full_url})) { + $url = $request_options->{full_url}; + } elsif (defined($request_options->{port}) && $request_options->{port} =~ /^[0-9]+$/) { $url = $request_options->{proto}. "://" . $request_options->{hostname} . ':' . $request_options->{port} . $request_options->{url_path}; } else { $url = $request_options->{proto}. "://" . $request_options->{hostname} . $request_options->{url_path}; @@ -304,7 +306,8 @@ sub request { $self->{output}->exit(); } - $self->{headers} = $response->headers; + $self->{headers} = $response->{headers}; + $self->{response} = $response; return $response->content; } @@ -314,4 +317,10 @@ sub get_header { return $self->{headers}; } +sub get_response { + my ($self, %options) = @_; + + return $self->{response}; +} + 1; diff --git a/notification/slack/mode/alert.pm b/notification/slack/mode/alert.pm new file mode 100644 index 000000000..6b5a2fe14 --- /dev/null +++ b/notification/slack/mode/alert.pm @@ -0,0 +1,343 @@ +# +# Copyright 2016 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::slack::mode::alert; + +use base qw(centreon::plugins::mode); + +use strict; +use warnings; +use centreon::plugins::http; +use JSON; + +my %slack_color_host = ( + up => 'good', + down => 'danger', + unreachable => 'danger', +); +my %slack_color_service = ( + ok => 'good', + warning => 'warning', + critical => 'danger', + unknown => 'warning', +); + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{version} = '1.0'; + $options{options}->add_options(arguments => + { + "slack-url:s" => { name => 'slack_url' }, + "slack-channel:s" => { name => 'slack_channel' }, + "slack-username:s" => { name => 'slack_username' }, + "host-name:s" => { name => 'host_name' }, + "host-state:s" => { name => 'host_state' }, + "host-output:s" => { name => 'host_output' }, + "service-description:s" => { name => 'service_description' }, + "service-state:s" => { name => 'service_state' }, + "service-output:s" => { name => 'service_output' }, + "slack-color:s" => { name => 'slack_color' }, + "slack-emoji:s" => { name => 'slack_emoji', }, + "graph-url:s" => { name => 'graph_url' }, + "priority:s" => { name => 'priority' }, + "zone:s" => { name => 'zone' }, + "link-url:s" => { name => 'link_url' }, + "centreon-url:s" => { name => 'centreon_url' }, + "centreon-token:s" => { name => 'centreon_token' }, + + "credentials" => { name => 'credentials' }, + "ntlm" => { name => 'ntlm' }, + "username:s" => { name => 'username' }, + "password:s" => { name => 'password' }, + "proxyurl:s" => { name => 'proxyurl' }, + "proxypac:s" => { name => 'proxypac' }, + "timeout:s" => { name => 'timeout' }, + }); + $self->{http} = centreon::plugins::http->new(output => $self->{output}); + $self->{payload_attachment} = { fields => [] }; + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::init(%options); + + if (!defined($self->{option_results}->{slack_url}) || $self->{option_results}->{slack_url} eq '') { + $self->{output}->add_option_msg(short_msg => "You need to specify --slack-url option."); + $self->{output}->option_exit(); + } + if (!defined($self->{option_results}->{slack_channel}) || $self->{option_results}->{slack_channel} eq '') { + $self->{output}->add_option_msg(short_msg => "You need to specify --slack-channel option."); + $self->{output}->option_exit(); + } + if (!defined($self->{option_results}->{host_name}) || $self->{option_results}->{host_name} eq '') { + $self->{output}->add_option_msg(short_msg => "You need to specify --host-name option."); + $self->{output}->option_exit(); + } + + foreach (('graph_url', 'link_url')) { + if (defined($self->{option_results}->{$_})) { + $self->{option_results}->{$_} =~ s/%\{(.*?)\}/\$self->{option_results}->{$1}/g; + eval "\$self->{option_results}->{\$_} = \"$self->{option_results}->{$_}\""; + } + } + + $self->{http}->set_options(%{$self->{option_results}}, hostname => 'dummy'); +} + +sub format_payload { + my ($self, %options) = @_; + + my $json = JSON->new; + my $payload = { channel => $self->{option_results}->{slack_channel}, + attachments => [ $self->{payload_attachment} ] }; + if (defined($self->{option_results}->{slack_emoji}) && $self->{option_results}->{slack_emoji} ne '') { + $payload->{icon_emoji} = $self->{option_results}->{slack_emoji}; + } + if (defined($self->{option_results}->{slack_username}) && $self->{option_results}->{slack_username} ne '') { + $payload->{username} = $self->{option_results}->{slack_username}; + } + eval { + $self->{payload_str} = $json->encode($payload); + }; + if ($@) { + $self->{output}->add_option_msg(short_msg => "Cannot decode json response"); + $self->{output}->option_exit(); + } +} + +sub host_message { + my ($self, %options) = @_; + + my $url_host = $self->{option_results}->{host_name}; + if (defined($self->{option_results}->{link_url}) && $self->{option_results}->{link_url} ne '') { + $url_host = '<' . $self->{option_results}->{link_url} . '|' . $self->{option_results}->{host_name} . '>'; + $self->{payload_attachment}->{fallback} = "Host " . $self->{option_results}->{host_name}; + } + $self->{payload_attachment}->{text} = "Host " . $url_host; + + if (defined($self->{option_results}->{host_state}) && $self->{option_results}->{host_state} ne '') { + $self->{payload_attachment}->{text} .= ' is ' . $self->{option_results}->{host_state}; + $self->{payload_attachment}->{fallback} .= ' is ' . $self->{option_results}->{host_state}; + if (defined($slack_color_host{lc($self->{option_results}->{host_state})})) { + $self->{payload_attachment}->{color} = $slack_color_host{lc($self->{option_results}->{host_state})}; + } + } else { + $self->{payload_attachment}->{text} .= ' alert'; + $self->{payload_attachment}->{fallback} .= ' alert'; + } + + if (defined($self->{option_results}->{link_url}) && $self->{option_results}->{link_url} ne '') { + $self->{payload_attachment}->{fallback} .= ' : ' . $self->{option_results}->{link_url}; + } + + if (defined($self->{option_results}->{host_output}) && $self->{option_results}->{host_output} ne '') { + push @{$self->{payload_attachment}->{fields}}, { title => 'output', value => $self->{option_results}->{host_output} }; + } +} + +sub service_message { + my ($self, %options) = @_; + + my $url_service = $self->{option_results}->{service_description}; + if (defined($self->{option_results}->{link_url}) && $self->{option_results}->{link_url} ne '') { + $url_service = '<' . $self->{option_results}->{link_url} . '|' . $self->{option_results}->{host_name} . '/' . $self->{option_results}->{service_description} . '>'; + $self->{payload_attachment}->{fallback} = "Service " . $self->{option_results}->{host_name} . '/' . $self->{option_results}->{service_description}; + } + $self->{payload_attachment}->{text} = "Service " . $url_service; + + if (defined($self->{option_results}->{service_state}) && $self->{option_results}->{service_state} ne '') { + $self->{payload_attachment}->{text} .= ' is ' . $self->{option_results}->{service_state}; + $self->{payload_attachment}->{fallback} .= ' is ' . $self->{option_results}->{service_state}; + if (defined($slack_color_service{lc($self->{option_results}->{service_state})})) { + $self->{payload_attachment}->{color} = $slack_color_service{lc($self->{option_results}->{service_state})}; + } + } else { + $self->{payload_attachment}->{text} .= ' alert'; + $self->{payload_attachment}->{fallback} .= ' alert'; + } + + if (defined($self->{option_results}->{link_url}) && $self->{option_results}->{link_url} ne '') { + $self->{payload_attachment}->{fallback} .= ' : ' . $self->{option_results}->{link_url}; + } + + if (defined($self->{option_results}->{service_output}) && $self->{option_results}->{service_output} ne '') { + push @{$self->{payload_attachment}->{fields}}, { title => 'output', value => $self->{option_results}->{service_output} }; + } + + if (defined($self->{option_results}->{graph_url}) && $self->{option_results}->{graph_url} ne '') { + $self->{payload_attachment}->{image_url} = $self->{option_results}->{graph_url}; + } +} + +sub set_payload { + my ($self, %options) = @_; + + if (!defined($self->{option_results}->{service_description}) && $self->{option_results}->{service_description} ne '') { + $self->host_message(); + } else { + $self->service_message(); + } + + if (defined($self->{option_results}->{slack_color}) && $self->{option_results}->{slack_color} ne '') { + $self->{payload_attachment}->{color} = $self->{option_results}->{slack_color}; + } + + if (defined($self->{option_results}->{priority}) && $self->{option_results}->{priority} ne '') { + push @{$self->{payload_attachment}->{fields}}, { title => 'Priority', value => $self->{option_results}->{priority}, short => 'true' }; + } + if (defined($self->{option_results}->{zone}) && $self->{option_results}->{zone} ne '') { + push @{$self->{payload_attachment}->{fields}}, { title => 'Zone', value => $self->{option_results}->{zone}, short => 'true' }; + } +} + +sub run { + my ($self, %options) = @_; + + $self->set_payload(); + $self->format_payload(); + my $response = $self->{http}->request(full_url => $self->{option_results}->{slack_url}, + method => 'POST', + post_param => ['payload=' . $self->{payload_str}]); + + $self->{output}->output_add(short_msg => 'slack response: ' . $response); + $self->{output}->display(nolabel => 1, force_ignore_perfdata => 1, force_long_output => 1); + $self->{output}->exit(); +} + +1; + +__END__ + +=head1 MODE + +Send slack alerts. + +Example for a host: +centreon_plugins.pl --plugin=notification::slack::plugin --mode=alert --slack-url='https://hooks.slack.com/services/T0A754E2V/B0E0CEL4B/81V8kCJusL7kafDSdsd' --slack-channel='#testchannel' --slack-username='bot' --slack-emoji=':ghost:' --host-name='srvi-clus-win' --host-state='DOWN' --host-output='test output' --priority='High' --zone='Production' --centreon-url='https://centreon.test.com/centreon/' --link-url='%{centreon_url}/main.php?p=20202&o=svc&host_search=%{host_name}' + +Example for a service: +centreon_plugins.pl --plugin=notification::slack::plugin --mode=alert --slack-url='https://hooks.slack.com/services/T0A754E2V/B0E0CEL4B/81V8kCJusL7kafDSdsd' --slack-channel='#tmptestqga' --slack-username='bot' --slack-emoji=':ghost:' --host-name='srvi-clus-win' --service-description='Ping' --service-state='WARNING' --service-output='CRITICAL - 10.50.1.78: rta nan, lost 100%' --priority='High' --zone='Production' --centreon-url='https://ces.merethis.net/centreon/' --link-url='%{centreon_url}/main.php?p=20201&o=svc&host_search=%{host_name}&svc_search=%{service_description}' --centreon-token='LxTQxFbLU6' --graph-url='%{centreon_url}/include/views/graphs/generateGraphs/generateImage.php?username=myuser&token=%{centreon_token}&hostname=%{host_name}&service=%{service_description}' + +=over 8 + +=item B<--slack-url> + +Specify slack url (Required). + +=item B<--slack-channel> + +Specify slack channel (Required). + +=item B<--slack-username> + +Specify slack username. + +=item B<--host-name> + +Specify host server name for the alert (Required). + +=item B<--host-state> + +Specify host server state for the alert. + +=item B<--host-output> + +Specify host server output message for the alert. + +=item B<--service-description> + +Specify service description name for the alert. + +=item B<--service-state> + +Specify service state for the alert. + +=item B<--service-output> + +Specify service output message for the alert. + +=item B<--slack-color> + +Specify slack color (According state option, color will be choosed). + +=item B<--slack-emoji> + +Specify slack emoji. + +=item B<--priority> + +Specify the priority message. + +=item B<--zone> + +Specify the zone message. + +=item B<--centreon-url> + +Specify the centreon url macro (could be used in link-url and graph-url option). + +=item B<--centreon-token> + +Specify the centreon token for autologin macro (could be used in link-url and graph-url option). + +=item B<--graph-url> + +Specify the graph url (Example: %{centreon_url}/include/views/graphs/generateGraphs/generateImage.php?username=myuser&token=%{centreon_token}&hostname=%{host_name}&service=%{service_description}). + +=item B<--link-url> + +Specify the link url (Example: %{centreon_url}/main.php?p=20201&o=svc&host_search=%{host_name}&svc_search=%{service_description}) + +=item B<--proxyurl> + +Proxy URL + +=item B<--proxypac> + +Proxy pac file (can be an url or local file) + +=item B<--credentials> + +Specify this option if you access webpage over basic authentification + +=item B<--ntlm> + +Specify this option if you access webpage over ntlm authentification (Use with --credentials option) + +=item B<--username> + +Specify username for basic authentification (Mandatory if --credentials is specidied) + +=item B<--password> + +Specify password for basic authentification (Mandatory if --credentials is specidied) + +=item B<--timeout> + +Threshold for HTTP timeout (Default: 5) + +=back + +=cut diff --git a/notification/slack/plugin.pm b/notification/slack/plugin.pm new file mode 100644 index 000000000..b221d5af5 --- /dev/null +++ b/notification/slack/plugin.pm @@ -0,0 +1,48 @@ +# +# Copyright 2016 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::slack::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} = '0.1'; + %{$self->{modes}} = ( + 'alert' => 'notification::slack::mode::alert', + ); + + return $self; +} + +1; + +__END__ + +=head1 PLUGIN DESCRIPTION + +Send Slack notifications. + +=cut