diff --git a/doc/source/06_medium/html-and-detailled-notifications.rst b/doc/source/06_medium/html-and-detailled-notifications.rst index 17b063877a..a0a34b6abe 100644 --- a/doc/source/06_medium/html-and-detailled-notifications.rst +++ b/doc/source/06_medium/html-and-detailled-notifications.rst @@ -7,9 +7,9 @@ Tired of brut text email notification, want to add more details to them and even .. image:: /_static/images/shinken-html-notification.png -Here it is the command you can use to have a basic html notification. You have to specify usual shinken macros in option. If not specified then it will try to get them from environment variable if you have set option ``enable_environment_macros`` in ``shinken.cfg`` if you want to used them. It isn't recommanded to use them for large environment. You better use option ``-c``, ``-o`` and ``-s`` or ``-h`` depend on which object you'll notify. Each macros separated by double comma. +Here it is the command you can use to have a basic html notification. You have to specify usual shinken macros in option. If not specified then it will try to get them from environment variable if you have set option ``enable_environment_macros`` in ``shinken.cfg``. It isn't recommended to use them for large environment. You better use option ``-c``, ``-o`` and ``-s`` or ``-h`` depending upon which object you'll notify. Each macros is separated by double comma. -Here is commands examples ready to use with it : +Here is commands examples ready to use: :: @@ -24,11 +24,41 @@ Here is commands examples ready to use with it : command_line $PLUGINSDIR$/notify_by_email.py -n service -S localhost -r $CONTACTEMAIL$ -f html -c "$NOTIFICATIONTYPE$,,$HOSTNAME$,,$HOSTADDRESS$,,$LONGDATETIME$" -o "$SERVICEDESC$,,$SERVICESTATE$,,$SERVICEOUTPUT$,,$SERVICEDURATION$" } +Text mail +~~~~~~~~~~~~~~~~~~~~ + +Despite the nice HTML formatting, you still prefer to use plain old text mail, use the `` -f text`` parameter instead of ``if html``. + +Mail sender +~~~~~~~~~~~~~~~~~~~~ + +Default mail sender is built automatically with current server name and current Shinken user. + +If you want to specif the mail sender, use the ``-s (--sender)``. For example: ``-s me@myserver.com``. + + Add an header logo ~~~~~~~~~~~~~~~~~~~~ -Let's say that you want to deliver mail to a customer and integrate its logo into, you only have to get a logo, named it ``customer_logo.png`` and paste it into ``/var/lib/shinken/share/images/``. +Let's say that you want to deliver mail to a customer and integrate host logo into the mail, you only have to get a logo, name it ``customer_logo.png`` and paste it into ``/var/lib/shinken/share/images/``. + +If WebUI2 is installed, then the script will try to find the company logo that can be defined in the WebUI2. The WebUI2 company logo is PNG file located in ``/var/lib/shinken/share/photos/`` and which name is defined in ``/etc/shinken/modules/webui2.cfg``, property ``company_logo``. + + +Add a WebUI link +~~~~~~~~~~~~~~~~~~~~ + +The host or service Web UI link can be included in the mail body. Simply use ``-w (--webui)`` option and you will get a direct link to the host or service page in the Web UI. + +The link is built using the current server name and the port defined in the Web UI configuration file. +If WebUI2 is installed, the script will try to find the port number defined in ``/etc/shinken/modules/webui2.cfg``, property ``port``. +If WebUI1 is installed, the script will try to find the port number defined in ``/etc/shinken/modules/webui.cfg``, property ``port``. +If both of them are installed, priority is WebUI2. + +If neither WebUI2 nor WebUI are installed on your server, there will be no link in the mail even if this option is used. + +If you want to define the root part of the URL, use option ``-u (--url)``. For example: ``-u http://webui.myserver:8080/shinken`` will be used as root part instead of ``http://shinken:7767``. Detailled Notifications -------------------------- @@ -39,7 +69,7 @@ Detailled notifications is a way to customize and add useful informations in ema - **_IMPACT** : Specify what will be the impact - **_FIXACTIONS** : And what is the recommended actions to do when there is an alert about it. -Example +Example ~~~~~~~~ For example, you can have the below configuration: @@ -47,7 +77,7 @@ our service : :: - + define service { service_description Cpu use 20min_long,linux-service @@ -92,6 +122,6 @@ And now define our notification ways: host_notification_options d,u,r,f,s service_notification_commands detailled-service-by-email ; send service notifications via email host_notification_commands detailled-host-by-email ; send host notifications via email - } + } Then you'll receive a nice html mail giving all your details in a table. diff --git a/etc/commands/notify-host-by-email.cfg b/etc/commands/notify-host-by-email.cfg index fdb0ce7168..9e93d37ab3 100644 --- a/etc/commands/notify-host-by-email.cfg +++ b/etc/commands/notify-host-by-email.cfg @@ -1,5 +1,5 @@ ## Notify Host by Email define command { command_name notify-host-by-email - command_line $PLUGINSDIR$/notify_by_email.py -n host -S localhost -r $CONTACTEMAIL$ -f html -c "$NOTIFICATIONTYPE$,,$HOSTNAME$,,$HOSTADDRESS$,,$LONGDATETIME$" -o "$HOSTALIAS$,,$HOSTSTATE$,,$HOSTDURATION$" + command_line $PLUGINSDIR$/notify_by_email.py -n host -S localhost -r $CONTACTEMAIL$ -f html -c "$NOTIFICATIONTYPE$,,$HOSTNAME$,,$HOSTADDRESS$,,$LONGDATETIME$" -o "$HOSTSTATE$,,$HOSTDURATION$" } diff --git a/libexec/notify_by_email.py b/libexec/notify_by_email.py index 295eafbcbb..a65358e5fd 100755 --- a/libexec/notify_by_email.py +++ b/libexec/notify_by_email.py @@ -36,6 +36,8 @@ shinken_customer_logo = 'customer_logo.jpg' webui_config_file = '/etc/shinken/modules/webui.cfg' +webui2_config_file = '/etc/shinken/modules/webui2.cfg' +webui2_image_dir = '/var/lib/shinken/share/photos' # Set up root logging def setup_logging(): @@ -50,39 +52,95 @@ def setup_logging(): def overload_test_variable(): shinken_notification_object_var = { 'service': { - 'Service description': 'CPU load', - 'Service state': 'PROBLEM', + 'Service description': 'Test_Service', + 'Service state': 'TEST', 'Service output': 'Houston, we got a problem here! Oh, wait. No. It\'s just a test.', - 'Service duration': '00h 00min 10s' + 'Service state duration': '00h 00min 10s' }, 'host': { - 'Hostname': 'Shinken monitoring solution: Test host', - 'Host state': 'TEST PROBLEM', - 'Host duration': '00h 00h 20s' + 'Hostname': 'Test_Host', + 'Host state': 'TEST', + 'Host state duration': '00h 00h 20s' } } shinken_var = { - 'Hostname': 'shinken.domain.tld', + 'Hostname': 'shinken', 'Host address': '127.0.0.1', 'Notification type': 'TEST', 'Date': 'Now, test' } return (shinken_notification_object_var, shinken_var) +def get_webui_logo(): + company_logo='' + + try: + webui_config_fh = open(webui2_config_file) + except IOError: + # WebUI2 not installed ... + full_logo_path = os.path.join(shinken_image_dir, shinken_customer_logo) + if os.path.isfile(full_logo_path): + return full_logo_path + + if opts.webui: + # WebUI2 installed + logging.debug('Webui2 is installed') + webui_config = webui_config_fh.readlines() + for line in webui_config: + if 'company_logo' in line: + company_logo = line.rsplit('company_logo')[1].strip() + company_logo += '.png' + logging.debug('Found company logo property: %s', company_logo) + if company_logo: + full_logo_path = os.path.join(webui2_image_dir, company_logo) + if os.path.isfile(full_logo_path): + logging.debug('Found company logo file: %s', full_logo_path) + return full_logo_path + else: + logging.debug('File %s does not exist!', full_logo_path) + return '' + + return company_logo + def get_webui_port(): - webui_config_fh = open(webui_config_file) + port='' + + try: + webui_config_fh = open(webui2_config_file) + except IOError: + # WebUI2 not installed, try WebUI1 + try: + webui_config_fh = open(webui_config_file) + except IOError: + # No WebUI + return '' + else: + # WebUI1 installed + logging.debug('Webui1 is installed') + else: + # WebUI2 installed + logging.debug('Webui2 is installed') + + logging.debug('Webui file handler: %s' % (webui_config_fh)) webui_config = webui_config_fh.readlines() + logging.debug('Webui config: %s' % (webui_config)) for line in webui_config: if 'port' in line: - port = line.rsplit('port')[1] - return port.strip() + port = line.rsplit('port')[1].strip() + return port def get_shinken_url(): if opts.webui: hostname = socket.gethostname() webui_port = get_webui_port() - url = 'http://%s:%s/%s/%s' % (hostname, webui_port, opts.notification_object, urllib.quote(shinken_var['Hostname'])) + if not webui_port: + return + + if opts.webui_url: + url = '%s/%s/%s' % (opts.webui_url, opts.notification_object, urllib.quote(shinken_var['Hostname'])) + else: + url = 'http://%s:%s/%s/%s' % (hostname, webui_port, opts.notification_object, urllib.quote(shinken_var['Hostname'])) # Append service if we notify a service object if opts.notification_object == 'service': @@ -93,10 +151,10 @@ def get_shinken_url(): # Get current process user that will be the mail sender def get_user(): if opts.sender: - return opts.sender + return opts.sender else: - return '@'.join((getpass.getuser(), socket.gethostname())) - + return '@'.join((getpass.getuser(), socket.gethostname())) + ############################################################################# # Common mail functions and var @@ -106,7 +164,19 @@ def get_user(): # Construct mail subject field based on which object we notify def get_mail_subject(object): - mail_subject = { 'host': 'Host %s alert for %s since %s' % (shinken_notification_object_var['host']['Host state'], shinken_var['Hostname'], shinken_notification_object_var['host']['Host duration']), 'service': '%s on Host: %s about service %s since %s' % (shinken_notification_object_var['service']['Service state'], shinken_var['Hostname'], shinken_notification_object_var['service']['Service description'], shinken_notification_object_var['service']['Service duration'])} + mail_subject = { + 'host': 'Host %s alert for %s since %s' % ( + shinken_notification_object_var['host']['Host state'], + shinken_var['Hostname'], + shinken_notification_object_var['host']['Host state duration'] + ), + 'service': '%s on Host: %s about service %s since %s' % ( + shinken_notification_object_var['service']['Service state'], + shinken_var['Hostname'], + shinken_notification_object_var['service']['Service description'], + shinken_notification_object_var['service']['Service state duration'] + ) + } return mail_subject[object] @@ -135,7 +205,7 @@ def create_mail(format): msg['To'] = opts.receivers logging.debug('Subject: %s' % (opts.prefix + get_mail_subject(opts.notification_object))) msg['Subject'] = opts.prefix + get_mail_subject(opts.notification_object) - + return msg ############################################################################# @@ -148,7 +218,7 @@ def create_txt_message(msg): for k,v in sorted(shinken_var.iteritems()): txt_content.append(k + ': ' + v) - + # Add url at the end url = get_shinken_url() if url != None: @@ -160,7 +230,7 @@ def create_txt_message(msg): msg.attach(msgText) return msg - + ############################################################################# # Html creation lair ############################################################################# @@ -184,8 +254,12 @@ def create_html_message(msg): logging.debug('Grabbed Shinken URL : %s' % url) # Header part - html_content = ['''\r -\r'''] - - full_logo_path = os.path.join(shinken_image_dir, shinken_customer_logo) - if os.path.isfile(full_logo_path): +th.customer {width: 600px; background-color: #004488; color: #ffffff;}\r +\r +\r +\r''' + ] + + full_logo_path = get_webui_logo() + if full_logo_path: msg = add_image2mail(full_logo_path, msg) html_content.append('') html_content.append('' % mail_welcome) @@ -228,10 +306,15 @@ def create_html_message(msg): # Make final string var to send and encode it to stdout encoding # avoiding decoding error. html_content = '\r\n'.join(html_content) - html_msg = html_content.encode(sys.stdout.encoding) + try: + html_msg = html_content.encode(sys.stdout.encoding) + except UnicodeDecodeError as e: + logging.debug('Content is Unicode encoded.') + html_msg = html_content.decode('utf-8').encode(sys.stdout.encoding) + logging.debug('HTML string: %s' % html_msg) - + msgText = MIMEText(html_msg, 'html') logging.debug('MIMEText: %s' % msgText) msg.attach(msgText) @@ -242,51 +325,62 @@ def create_html_message(msg): if __name__ == "__main__": parser = OptionParser(description='Notify by email receivers of Shinken alerts. Message will be formatted in html and can embed customer logo. To included customer logo, just load png image named customer_logo.png in '+shinken_image_dir) - group_debug = OptionGroup(parser, 'Debugging and test options', 'Useful to debugging script under shinken processes. Useful to just make a standalone test of script to see what it looks like.') - group_shinken_details = OptionGroup(parser, 'Details and additionnals informations', 'You can include some useful additionnals informations to notification using these options. Good practice is to add HOST or SERVICE macros with these details and pass them to the script') - group_shinken = OptionGroup(parser, 'Shinken macros to specify.', 'Used to specify usual shinken macros in notify, if not specified then it will try to get them from environment variable. You need to enable_environment_macros in shinken.cfg if you want to used them. It isn\'t recommanded to used them for large environment. You better use option -c and -s or -h depend on which object you\'ll notify.') + group_debug = OptionGroup(parser, 'Debugging and test options', 'Useful to debug script under shinken processes. Useful to just make a standalone test of script to see what it looks like.') group_general = OptionGroup(parser, 'General options', 'Default options to setup') + group_shinken = OptionGroup(parser, 'Shinken macros to specify.', 'Used to specify usual shinken macros in notifications, if not specified then it will try to get them from environment variable. You need to enable_environment_macros in shinken.cfg if you want to used them. It isn\'t recommended to use environment macros for large environments. You \'d better use options -c and -s or -h depending on which object you\'ll notify for.') + group_shinken_details = OptionGroup(parser, 'Details and additional information', 'You can include some useful additional information to notifications using these options. Good practice is to add HOST or SERVICE macros with these details and provide them to the script') + group_shinken_webui = OptionGroup(parser, 'Shinken WebUI.', 'Used to include some Shinken WebUI information in the notifications.') + # Debug and test options group_debug.add_option('-D', '--debug', dest='debug', default=False, action='store_true', help='Generate a test mail message') - group_debug.add_option('-t', '--test', dest='test', + group_debug.add_option('-t', '--test', dest='test', default=False, action='store_true', help='Generate a test mail message') - group_general.add_option('-w', '--webui', dest='webui', default=False, - action='store_true', help='Include link to the problem in Shinken WebUI.') - group_general.add_option('-f', '--format', dest='format', type='choice', choices=['txt', 'html'], - default='html', help='Mail format "html" or "txt". Default: html') group_debug.add_option('-l', '--logfile', dest='logfile', help='Specify a log file. Default: log to stdout.') + + # General options + group_general.add_option('-f', '--format', dest='format', type='choice', choices=['txt', 'html'], + default='html', help='Mail format "html" or "txt". Default: html') + group_general.add_option('-r', '--receivers', dest='receivers', + help='Mail recipients comma-separated list') + group_general.add_option('-F', '--sender', dest='sender', + help='Sender email address, default is system user') + group_general.add_option('-S', '--SMTP', dest='smtp', default='localhost', + help='Target SMTP hostname. None for just a sendmail lanch. Default: localhost') + group_general.add_option('-p', '--prefix', dest='prefix', default='', + help='Mail subject prefix. Default is no prefix') + + # Shinken options + group_shinken.add_option('-n', '--notification-object', dest='notification_object', type='choice', default='host', + choices=['host', 'service'], help='Choose between host or service notification.') group_shinken.add_option('-c', '--commonmacros', dest='commonmacros', help='Double comma separated shinken macros in this order : "NOTIFICATIONTYPE$,,$HOSTNAME$,,$HOSTADDRESS$,,$LONGDATETIME$".') group_shinken.add_option('-o', '--objectmacros', dest='objectmacros', - help='Double comma separated object shinken macros in this order : "$SERVICEDESC$,,$SERVICESTATE$,,$SERVICEOUTPUT$,,$SERVICEDURATION$" for a service object and "$HOSTSTATE$,,$HOSTDURATION$" with host object') + help='Double comma separated object shinken macros in this order : "$SERVICEDESC$,,$SERVICESTATE$,,$SERVICEOUTPUT$,,$SERVICEDURATION$" for a service object and "$HOSTSTATE$,,$HOSTDURATION$" for an host object') group_shinken_details.add_option('-d', '--detailleddesc', dest='detailleddesc', help='Specify $_SERVICEDETAILLEDDESC$ custom macros') group_shinken_details.add_option('-i', '--impact', dest='impact', help='Specify the $_SERVICEIMPACT$ custom macros') group_shinken_details.add_option('-a', '--action', dest='fixaction', help='Specify the $_SERVICEFIXACTIONS$ custom macros') - group_general.add_option('-r', '--receivers', dest='receivers', - help='Mail recipients comma-separated list') - group_general.add_option('-F', '--sender', dest='sender', - help='Sender email address, default is system user') - group_general.add_option('-n', '--notification-object', dest='notification_object', type='choice', default='host', - choices=['host', 'service'], help='Choose between host or service notification.') - group_general.add_option('-S', '--SMTP', dest='smtp', default='localhost', - help='Target SMTP hostname. Default: localhost') - group_general.add_option('-p', '--prefix', dest='prefix', default='', - help='Mail subject prefix. Default is no prefix') - + + # Shinken WebUI options + group_shinken_webui.add_option('-w', '--webui', dest='webui', default=False, + action='store_true', help='Include link to the problem in Shinken WebUI.') + group_shinken_webui.add_option('-u', '--url', dest='webui_url', + help='WebUI URL as http://my_webui:port/url') + parser.add_option_group(group_debug) parser.add_option_group(group_general) parser.add_option_group(group_shinken) parser.add_option_group(group_shinken_details) + parser.add_option_group(group_shinken_webui) (opts, args) = parser.parse_args() - + setup_logging() - + # Check and process arguments # # Retrieve and setup shinken macros that make the mail content @@ -307,44 +401,44 @@ def create_html_message(msg): } if opts.objectmacros == None: - shinken_notification_object_var = { + shinken_notification_object_var = { 'service': { 'Service description': os.getenv('NAGIOS_SERVICEDESC'), 'Service state': os.getenv('NAGIOS_SERVICESTATE'), 'Service output': os.getenv('NAGIOS_SERVICEOUTPUT'), - 'Service duration': os.getenv('NAGIOS_SERVICEDURATION') + 'Service state duration': os.getenv('NAGIOS_SERVICEDURATION') }, 'host': { - 'Host state': os.getenv('NAGIOS_HOSTSTATE'), - 'Host duration': os.getenv('NAGIOS_HOSTDURATION') + 'Host state': os.getenv('NAGIOS_HOSTSTATE'), + 'Host state duration': os.getenv('NAGIOS_HOSTDURATION') } } else: macros = opts.objectmacros.split(',,') if opts.notification_object == 'service': - shinken_notification_object_var = { + shinken_notification_object_var = { 'service': { 'Service description': macros[0], 'Service state': macros[1], 'Service output': macros[2], - 'Service duration': macros[3] + 'Service state duration': macros[3] }, 'host': { - 'Host state': '', - 'Host duration': '' + 'Host state': '', + 'Host state duration': '' } } else: - shinken_notification_object_var = { + shinken_notification_object_var = { 'service': { 'Service description': '', 'Service state': '', 'Service output': '', - 'Service duration': '' + 'Service state duration': '' },'host': { 'Host state': macros[0], - 'Host duration': macros[1] + 'Host state duration': macros[1] } } @@ -354,13 +448,13 @@ def create_html_message(msg): # check required arguments if opts.receivers == None: - logging.error('You must defined a mail recipient using -r') + logging.error('You must define at least one mail recipient using -r') sys.exit(5) else: contactemail = opts.receivers - - + + if opts.detailleddesc: shinken_var['Detailled description'] = opts.detailleddesc.decode(sys.stdin.encoding) if opts.impact: @@ -369,7 +463,7 @@ def create_html_message(msg): shinken_var['Fix actions'] = opts.fixaction.decode(sys.stdin.encoding) receivers = make_receivers_list(opts.receivers) - + logging.debug('Create mail skeleton') mail = create_mail(opts.format) logging.debug('Create %s mail content' % (opts.format)) @@ -378,8 +472,22 @@ def create_html_message(msg): elif opts.format == 'txt': mail = create_txt_message(mail) - logging.debug('Connect to %s smtp server' % (opts.smtp)) - smtp = smtplib.SMTP(opts.smtp) - logging.debug('Send the mail') - smtp.sendmail(get_user(), receivers, mail.as_string()) - logging.info("Mail sent successfuly") + # Use SMTP or sendmail to send the mail ... + if opts.smtp != 'None': + logging.debug('Connect to %s smtp server' % (opts.smtp)) + smtp = smtplib.SMTP(opts.smtp) + logging.debug('Send the mail') + smtp.sendmail(opts.smtpfrom, receivers, mail.as_string()) + logging.info("Mail sent successfuly") + else: + sendmail = '/usr/sbin/sendmail' + logging.debug('Send the mail') + p = os.popen('%s -t' % sendmail, 'w') + logging.debug('Final mail : ' + mail.as_string()) + logging.debug('Send the mail') + p.write(mail.as_string()) + status = p.close() + if status is not None: + logging.error("Sendmail returned %s" % status) + else: + logging.info("Mail sent successfuly") \ No newline at end of file
%s