# # Copyright 2021 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; $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' }, "basic" => { name => 'basic' }, "ntlm" => { name => 'ntlm' }, "username:s" => { name => 'username' }, "password:s" => { name => 'password' }, "timeout:s" => { name => 'timeout' } }); $self->{http} = centreon::plugins::http->new(%options); $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}/eg; } } $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}; $self->{payload_attachment}->{fallback} = "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}->{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}, short => 'true' }; } if (defined($self->{option_results}->{host_state}) && $self->{option_results}->{host_state} ne '') { push @{$self->{payload_attachment}->{fields}}, { title => 'State', value => $self->{option_results}->{host_state}, short => 'true'}; } } sub service_message { my ($self, %options) = @_; my $url_service = "Host: " . $self->{option_results}->{host_name} . " | Service " . $self->{option_results}->{service_description}; $self->{payload_attachment}->{fallback} = $url_service; 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} = $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}; } if (defined($self->{option_results}->{service_state}) && $self->{option_results}->{service_state} ne '') { push @{$self->{payload_attachment}->{fields}}, { title => 'State', value => $self->{option_results}->{service_state}, short => 'true'}; } } sub set_payload { my ($self, %options) = @_; if (defined($self->{option_results}->{service_description}) && $self->{option_results}->{service_description} ne '') { $self->service_message(); } else { $self->host_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<--credentials> Specify this option if you access webpage with authentication =item B<--username> Specify username for authentication (Mandatory if --credentials is specified) =item B<--password> Specify password for authentication (Mandatory if --credentials is specified) =item B<--basic> Specify this option if you access webpage over basic authentication and don't want a '401 UNAUTHORIZED' error to be logged on your webserver. Specify this option if you access webpage over hidden basic authentication or you'll get a '404 NOT FOUND' error. (Use with --credentials) =item B<--timeout> Threshold for HTTP timeout (Default: 5) =back =cut