├── .gitignore ├── LICENSE ├── README.md ├── bacula-jobs-json.cache ├── conf-bacula-dir-messages ├── conf-zabbix_agentd-userparam ├── conf.py ├── get-bacula-jobs-json.py ├── notify.py ├── notify_operator.py └── tmpl bacula-director.xml /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 14 rue de Plaisance, 75014 Paris, France 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | WARNING: https://www.bareos.org/en/faq/why_fork.html You may be interested in using Bareos instead of original Bacula. 2 | 3 | NOTICE: germanodlf created analogous tool based on this, which seems more mature and feature-reach: [germanodlf/bacula-zabbix](https://github.com/germanodlf/bacula-zabbix) You may be interested to check it out. 4 | 5 | bacula_zabbix_integration 6 | ========================= 7 | 8 | Scripts and template to integrate bacula with zabbix. 9 | 10 | Abilities 11 | --------- 12 | * separate monitoring for each job 13 | * low-level auto-discovery of new jobs 14 | 15 | Workflow 16 | --------- 17 | For each job it's exit status and parameters are forwarded to Zabbix. 18 | 19 | Triggers 20 | -------- 21 | * Job exit status indicates error 22 | * Job was not launched for 36 hours 23 | * FD non-fatal errors occured 24 | * SD errors occured 25 | * Verify job failed 26 | 27 | Installation 28 | ------------ 29 | 30 | * Copy file somewhere. Default is /etc/bacula/bacula-to-zabbix 31 | * Make sure that zabbix user can launch bconsole and get output of 'show jobs' command. 32 | * Tweak conf.py: 33 | * path to zabbix_sender 34 | * bconsole config file 35 | * jobs list cache file. (!) This file should be avaliable for write to zabbix user. 36 | * timeout for bconsole command in seconds (default 5 seconds) 37 | * hostname for sending messages to zabbix 38 | * Add UserParameter from to zabbix_agentd.conf. Example in file conf-zabbix_agentd-userparam. Restart zabbix_agentd 39 | * Config Messages resuorce in bacula-director.conf. Example in file conf-bacula-dir-messages. You can directly include this file with @/etc/bacula/bacula-to-zabbix/conf-bacula-dir-messages. Reload config for bacula-director 40 | * Add template tmpl bacula-director.xml to zabbix. Assign it to host with bacula-director. 41 | * Disable auto-generated triggers for jobs that are not backup type(restore jobs, ...) 42 | 43 | Feedback 44 | -------- 45 | 46 | Feel free to send bug reports and feature requests [here](https://github.com/selivan/bacula_zabbix_integration/issues). 47 | 48 | **P.S.** If this code is useful for you - don't forget to put a star on it's [github repo](https://github.com/selivan/ansible_ipmi_lan_manage). 49 | -------------------------------------------------------------------------------- /bacula-jobs-json.cache: -------------------------------------------------------------------------------- 1 | stub 2 | -------------------------------------------------------------------------------- /conf-bacula-dir-messages: -------------------------------------------------------------------------------- 1 | # Message settings 2 | Messages { 3 | Name = Standard 4 | # mailcommand = "/usr/sbin/bsmtp -h localhost -f \"\(Bacula\) no-reply@localhost\" -s \"Bacula: %t %e of %c %l\" root@localhost" 5 | # operatorcommand = "/usr/sbin/bsmtp -h localhost -f \"\(Bacula\) no-reply@localhost\" -s \"Bacula: Intervention needed for %j\" root@localhost" 6 | # %n = Job name 7 | # %t = Job type (e.g. Backup, ...) 8 | # %l = Job level 9 | # %e = Job Exit code (OK, Error, ...) 10 | # %r = Recipients 11 | mailcommand = "/etc/bacula/bacula-to-zabbix/notify.py %n %t %l %e %r" 12 | operatorcommand = "/etc/bacula/bacula-to-zabbix/notify_operator.py %r mgmt-sc" 13 | # send messages to zabbix 14 | mail = = all, !skipped 15 | # send mount messages to zabbix 16 | operator = = mount 17 | # send messages to console 18 | console = all, !skipped, !saved 19 | # save messages to database - for webacula 20 | catalog = all, !skipped, !saved 21 | # log messages to file 22 | append = /var/log/bacula/log = all, !skipped 23 | } 24 | -------------------------------------------------------------------------------- /conf-zabbix_agentd-userparam: -------------------------------------------------------------------------------- 1 | UserParameter=bacula.jobs,/etc/bacula/bacula-to-zabbix/get-bacula-jobs-json.py 2 | -------------------------------------------------------------------------------- /conf.py: -------------------------------------------------------------------------------- 1 | from socket import gethostname 2 | 3 | conf={ 4 | 'zabbix_sender':"/usr/bin/zabbix_sender", 5 | 'jobs_cache_file':"/etc/bacula/bacula-to-zabbix/bacula-jobs-json.cache", 6 | 'bconsole_conf_file':"/etc/bacula/bconsole.conf", 7 | 'bconsole_wait':5 8 | } 9 | conf['hostname']=gethostname() 10 | 11 | -------------------------------------------------------------------------------- /get-bacula-jobs-json.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import subprocess 4 | from conf import conf 5 | 6 | command="echo quit | timeout %d bconsole -c '%s'" % (conf['bconsole_wait'], conf['bconsole_conf_file']) 7 | exit_code=subprocess.Popen(command,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE).wait() 8 | if exit_code != 0: 9 | print file(conf['jobs_cache_file']).read() 10 | quit(1) 11 | 12 | out=open(conf['jobs_cache_file'],'w') 13 | out.write('{ "data": [ \n') 14 | 15 | command="echo show jobs | timeout %d bconsole -c %s | awk '/^Job/ {sub(\"name=\",\"\",$2); print $2}'" % (conf['bconsole_wait'], conf['bconsole_conf_file']) 16 | proc=subprocess.Popen(command, shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) 17 | jobs_raw=proc.communicate()[0] 18 | firstrun=1 19 | for job in jobs_raw.split('\n'): 20 | if job == '': continue 21 | if firstrun != 1: 22 | out.write(',\n') 23 | else: 24 | firstrun=0 25 | out.write("{ \"{#JOBNAME}\":\"%s\"}" % job) 26 | 27 | out.write(' ] }') 28 | out.close() 29 | 30 | print file(conf['jobs_cache_file']).read() 31 | 32 | -------------------------------------------------------------------------------- /notify.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ''' 3 | Simple script to send Bacula reports to Zabbix 4 | Made by Pavel Selivanov 5 | Forked by Valen Tau 6 | Should be used in Bacula-dir config instead of mail command: 7 | mail = <> = all, !skipped 8 | mailcommand = "/etc/zabbix/bacula_notify.py %n %t %l %e %r" 9 | operatorcommand = "/etc/zabbix/bacula_notify.py %r " 10 | Hostnames in Zabbix and Bacula must correspond 11 | ''' 12 | import sys 13 | import re 14 | import subprocess 15 | import datetime 16 | import os 17 | # Logging 18 | import logging 19 | logging.basicConfig( 20 | format=u'%(levelname)-8s [%(asctime)s] %(message)s', 21 | level=logging.DEBUG, 22 | filename=u'/var/log/bacula/{0}.log'.format(os.path.basename(__file__)) 23 | ) 24 | logging.info('sys.argv: ' + repr(sys.argv)) 25 | 26 | # Handle incorrect call 27 | if sys.version_info >= (3,): 28 | logging.warn("Need python version 2 to run. Tested with python 2.6") 29 | quit(1) 30 | 31 | if len(sys.argv) < 6: 32 | logging.warn( 33 | ( 34 | "Usage: %s " 35 | "JOB_NAME JOB_TYPE JOB_LEVEL JOB_EXIT_CODE ZABBIX_SERVER" 36 | ) % sys.argv[0] 37 | ) 38 | quit(5) 39 | 40 | # Settings 41 | from conf import conf 42 | 43 | # Get values from arguments 44 | job_name = sys.argv[1] 45 | result = {} 46 | result['bacula.job_type'] = sys.argv[2] 47 | result['bacula.job_level'] = sys.argv[3] 48 | result['bacula.job_exit_code'] = sys.argv[4] 49 | zabbix_server = sys.argv[5] 50 | 51 | # Define how to get values from input 52 | tests = ( 53 | 54 | ("\s*FD Files Written:\s+([0-9]+)\s*", 55 | "bacula.fd_fileswritten", 56 | lambda x: x.group(1)), 57 | 58 | ("\s*SD Files Written:\s+([0-9]+)\s*", 59 | "bacula.sd_fileswritten", 60 | lambda x: x.group(1)), 61 | 62 | ("\s*FD Bytes Written:\s+([0-9][,0-9]*)\s+\(.*\)\s*", 63 | "bacula.fd_byteswritten", 64 | lambda x: x.group(1).translate(None, ",")), 65 | 66 | ("\s*SD Bytes Written:\s+([0-9][,0-9]*)\.*", 67 | "bacula.sd_byteswritten", 68 | lambda x: x.group(1).translate(None, ",")), 69 | 70 | ("\s*Last Volume Bytes:\s+([0-9][,0-9]*).*", 71 | "bacula.lastvolumebytes", 72 | lambda x: x.group(1).translate(None, ",")), 73 | 74 | ("\s*Files Examined:\s+([0-9][,0-9]*)\s*", 75 | "bacula.verify_filesexamined", 76 | lambda x: x.group(1).translate(None, ",")), 77 | 78 | ("\s*Non-fatal FD errors:\s+([0-9]+)\s*", 79 | "bacula.fd_errors_non_fatal", 80 | lambda x: x.group(1)), 81 | 82 | ("\s*SD Errors:\s+([0-9]+)\s*", 83 | "bacula.sd_errors", 84 | lambda x: x.group(1)) 85 | 86 | ) 87 | 88 | # Get values from input 89 | for line in sys.stdin.readlines(): 90 | for regexp, key, value in tests: 91 | match = re.match(regexp, line) 92 | if match: 93 | # DEBUG 94 | logging.debug(line) 95 | result[key] = value(match) 96 | 97 | # DEBUG 98 | logging.debug(repr(result)) 99 | 100 | # Send result to zabbix 101 | for key, value in result.iteritems(): 102 | command = "{0} -z {1} -s {2} -k '{3}[{4}]' -o '{5}'".format( 103 | conf['zabbix_sender'], 104 | zabbix_server, 105 | conf['hostname'], 106 | key, 107 | job_name, 108 | value) 109 | logging.info(command) 110 | subprocess.call(command, shell=True) 111 | -------------------------------------------------------------------------------- /notify_operator.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ''' 3 | Simple script to send Bacula reports to Zabbix 4 | Made by Pavel Selivanov 5 | Forked by Valen Tau 6 | Should be used in Bacula-dir config instead of mail command: 7 | mail = <> = all, !skipped 8 | mailcommand = "/etc/zabbix/bacula_notify.py %r %c" 9 | operatorcommand = "/etc/zabbix/bacula_notify_operator.py %r " 10 | Hostnames in Zabbix and Bacula must correspond 11 | ''' 12 | import sys 13 | import re 14 | import os 15 | import datetime 16 | import subprocess 17 | # Logging 18 | import logging 19 | logging.basicConfig( 20 | format=u'%(levelname)-8s [%(asctime)s] %(message)s', 21 | level=logging.DEBUG, 22 | filename=u'/var/log/bacula/{0}.log'.format(os.path.basename(__file__)) 23 | ) 24 | logging.info('sys.argv: ') 25 | for _str in sys.argv: 26 | logging.info(_str) 27 | 28 | # Handle incorrect call 29 | if sys.version_info >= (3,): 30 | logging.warn("Need python version 2 to run. Tested with python 2.6") 31 | quit(1) 32 | 33 | if len(sys.argv) < 3: 34 | logging.warn( 35 | "Usage: {0} ZABBIX_SERVER HOSTNAME CUSTOM_MESSAGE".format(sys.argv[0])) 36 | quit(5) 37 | 38 | # Settings 39 | from conf import conf 40 | 41 | command = "{0} -z {1} -s {2} -k 'bacula.custommessage' -o '{3}'".format( 42 | conf['zabbix_sender'], 43 | sys.argv[1], 44 | conf['hostname'], 45 | sys.stdin.read() 46 | ) 47 | logging.info(command) 48 | subprocess.call(command, shell=True) 49 | -------------------------------------------------------------------------------- /tmpl bacula-director.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 2.0 4 | 2013-07-11T09:51:26Z 5 | 6 | 7 | Templates 8 | 9 | 10 | 11 | 494 | 495 | 496 | 497 | {bacula-director:bacula.custommessage.regexp("^0$")}=0 498 | Bacula custom message on {HOSTNAME}: {ITEM.LASTVALUE}. Set to 0 to reset trigger. 499 | 500 | 0 501 | 4 502 | 503 | 0 504 | 505 | 506 | 507 | 508 | --------------------------------------------------------------------------------