├── zabbix ├── MANIFEST.in ├── examples │ ├── zabbix_item_add_example.py │ ├── zabbix_rpc_test.py │ └── zabbix_screen.py ├── README.md ├── setup.py └── zabbix_api.py ├── .gitignore ├── README.md └── checks ├── README.md ├── functions.sh └── disksmart.sh /zabbix/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[co] 2 | .idea 3 | *.swp 4 | *~ 5 | build 6 | dist 7 | *.egg-info 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | python zabbix api - https://github.com/gescheit/scripts/tree/master/zabbix 2 | 3 | -------------------------------------------------------------------------------- /checks/README.md: -------------------------------------------------------------------------------- 1 | functions.sh - functions for locking and caching 2 | disksmart.sh - check SMART of all disks in system 3 | - Work under FreeBSD and linux 4 | - Use caching and locking 5 | -------------------------------------------------------------------------------- /zabbix/examples/zabbix_item_add_example.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on 01.10.2010 3 | 4 | @author: gescheit 5 | ''' 6 | from zabbix_api import ZabbixAPI 7 | 8 | server="http://127.0.0.1" 9 | username="api" 10 | password="apipass" 11 | 12 | zapi = ZabbixAPI(server=server, path="", log_level=6) 13 | zapi.login(username, password) 14 | 15 | host_name="test_host" 16 | 17 | description='Used disk space on $1 in %' 18 | key='vfs.fs.size[/,pused]' 19 | 20 | hostid=zapi.host.get({"filter":{"host":host_name}})[0]["hostid"] 21 | print hostid 22 | zapi.item.create({ 'hostid' : (hostid),'description' : (description),'key_' : key }) 23 | -------------------------------------------------------------------------------- /zabbix/README.md: -------------------------------------------------------------------------------- 1 | This is an implementation of the Zabbix API in Python. 2 | Please note that the Zabbix API is still in a draft state, 3 | and subject to change. 4 | 5 | Implementations of the Zabbix API in other languages may 6 | be found on the wiki. 7 | 8 | Zabbix 1.8, 2.0, 2.2, 2.4, 3.0 and 3.2 are supported. 9 | Python 2 and 3 are supported. 10 | 11 | Future versions must be supported too, if there is no deep changes. 12 | 13 | Installation: 14 | ```sh 15 | # pip install zabbix-api 16 | ``` 17 | 18 | Short example: 19 | 20 | ```python 21 | >>> from zabbix_api import ZabbixAPI 22 | >>> zapi = ZabbixAPI(server="https://server/") 23 | >>> zapi.login("login", "password") 24 | >>> zapi.trigger.get({"expandExpression": "extend", "triggerids": range(0, 100)}) 25 | ``` 26 | 27 | See also: 28 | * http://www.zabbix.com/wiki/doc/api 29 | * https://www.zabbix.com/documentation/2.4/manual/api 30 | * http://www.zabbix.com/forum/showthread.php?t=15218 31 | -------------------------------------------------------------------------------- /zabbix/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | Zabbix API 6 | """ 7 | import os 8 | from setuptools import setup, find_packages, findall 9 | 10 | 11 | def read_descr(fname): 12 | filepath = os.path.join(os.path.dirname(__file__), fname) 13 | try: 14 | import pypandoc 15 | long_description = pypandoc.convert_file(filepath, 'rst') 16 | except(IOError, ImportError): 17 | long_description = open(filepath).read() 18 | 19 | return long_description 20 | 21 | 22 | setup( 23 | name='zabbix-api', 24 | url='https://github.com/gescheit/scripts', 25 | version='0.5.6', 26 | license='GNU LGPL 2.1', 27 | author='Aleksandr Balezin', 28 | author_email='gescheit12@gmail.com', 29 | description='Zabbix API', 30 | long_description=read_descr('README.md'), 31 | py_modules=['zabbix_api'], 32 | include_package_data=True, 33 | zip_safe=False, 34 | platforms='any', 35 | classifiers=[ 36 | 'Programming Language :: Python', 37 | 'Programming Language :: Python :: 2', 38 | 'Programming Language :: Python :: 3', 39 | 'Development Status :: 5 - Production/Stable', 40 | ] 41 | ) 42 | -------------------------------------------------------------------------------- /checks/functions.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | LOCKDIR="/tmp" 4 | CACHEDIR=$LOCKDIR 5 | 6 | getFromCache() 7 | { 8 | CACHEFILE="$CACHEDIR/$1.cache" 9 | TIMEOUT="$2" 10 | CURRENTTIME="`date +%s`" 11 | if [ -s ${CACHEFILE} ]; then 12 | CACHEDATA=`cat ${CACHEFILE}` 13 | OLDDATATIME=${CACHEDATA%%,*} 14 | OLDDATA=${CACHEDATA#*,} 15 | 16 | if [ $OLDDATATIME -gt $((CURRENTTIME-TIMEOUT)) ]; then 17 | echo "$OLDDATA" 18 | return 0 19 | else 20 | return 1 21 | fi 22 | fi 23 | } 24 | 25 | writeToCache() 26 | { 27 | CACHEFILE="$CACHEDIR/$1.cache" 28 | DATA="$2" 29 | CURRENTTIME="`date +%s`" 30 | echo "$CURRENTTIME,$DATA" > "$CACHEFILE" 31 | [ "`id -u -n`" = "zabbix" ] || chown zabbix "$CACHEFILE" 32 | return 0 33 | } 34 | 35 | lockf() 36 | { 37 | LOCKFILE="$LOCKDIR/$1.lock" 38 | if [ -n "$2" ]; then 39 | RETRY=$2 40 | else 41 | RETRY=1 42 | fi 43 | while [ $RETRY -gt 0 ]; do 44 | RETRY=`expr $RETRY - 1` 45 | if (set -o noclobber; echo "$$" > "$LOCKFILE") 2> /dev/null; then 46 | trap 'rm -f "$LOCKFILE"; exit $?' INT TERM EXIT 47 | return 0 48 | fi 49 | if [ -f "$LOCKFILE" ]; then 50 | kill -0 `cat "$LOCKFILE"` 1>/dev/null 2>&1 51 | if [ $? -ne 0 ]; then 52 | rm -f "$LOCKFILE" 53 | if [ $? -ne 0 ]; then 54 | echo "unable to remove lock" 55 | return 1 56 | fi 57 | fi 58 | fi 59 | sleep 1 60 | done 61 | echo "Locking failed. Held by $(cat $LOCKFILE)" 62 | return 1 63 | } 64 | 65 | unlockf() 66 | { 67 | LOCKFILE="$LOCKDIR/$1.lock" 68 | rm -f "$LOCKFILE" 69 | trap - INT TERM EXIT 70 | return 0 71 | } 72 | -------------------------------------------------------------------------------- /zabbix/examples/zabbix_rpc_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import optparse 3 | import sys 4 | import traceback 5 | from getpass import getpass 6 | from zabbix_api import ZabbixAPI, ZabbixAPIException 7 | 8 | def get_options(): 9 | """ command-line options """ 10 | 11 | usage = "usage: %prog [options]" 12 | OptionParser = optparse.OptionParser 13 | parser = OptionParser(usage) 14 | 15 | parser.add_option("-s", "--server", action="store", type="string", \ 16 | dest="server", help="Zabbix Server URL (REQUIRED)") 17 | parser.add_option("-u", "--username", action="store", type="string", \ 18 | dest="username", help="Username (Will prompt if not given)") 19 | parser.add_option("-p", "--password", action="store", type="string", \ 20 | dest="password", help="Password (Will prompt if not given)") 21 | 22 | options, args = parser.parse_args() 23 | 24 | if not options.server: 25 | show_help(parser) 26 | 27 | if not options.username: 28 | options.username = raw_input('Username: ') 29 | 30 | if not options.password: 31 | options.password = getpass() 32 | 33 | # apply clue to user... 34 | if not options.username and not options.password: 35 | show_help(parser) 36 | 37 | return options, args 38 | 39 | def show_help(p): 40 | p.print_help() 41 | print "NOTE: Zabbix 1.8.0 doesn't check LDAP when authenticating." 42 | sys.exit(-1) 43 | 44 | def errmsg(msg): 45 | sys.stderr.write(msg + "\n") 46 | sys.exit(-1) 47 | 48 | if __name__ == "__main__": 49 | options, args = get_options() 50 | 51 | zapi = ZabbixAPI(server=options.server,log_level=3) 52 | 53 | try: 54 | zapi.login(options.username, options.password) 55 | print "Zabbix API Version: %s" % zapi.api_version() 56 | print "Logged in: %s" % str(zapi.test_login()) 57 | except ZabbixAPIException, e: 58 | sys.stderr.write(str(e) + '\n') 59 | 60 | try: 61 | for host in zapi.host.get({ 'monitored_hosts' : True,'extendoutput' : True}): 62 | if host['dns'] == "": 63 | print "%s - %s - %s" % (host['host'], host['ip'], host['useip']) 64 | else: 65 | print "%s - %s - %s" % (host['dns'], host['ip'], host['useip']) 66 | 67 | if host['useip'] == "1" and host['dns'] != "": 68 | print "Updating %s to monitor by FQDN." % host['dns'] 69 | newhost = host 70 | newhost['useip'] = 0 71 | zapi.host.update(newhost) 72 | 73 | except ZabbixAPIException, e: 74 | sys.stderr.write(str(e) + '\n') 75 | -------------------------------------------------------------------------------- /checks/disksmart.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # check SMART of all disks in system 3 | BASENAME=$(dirname $0) 4 | . ${BASENAME}/functions.sh 5 | PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/home/zabbix/bin 6 | RES="" 7 | ME="disksmart_check" 8 | TIMEOUT=600 9 | ERRLOG_COUNT_THRESHOLD=100 10 | if [ `which smartctl >/dev/null; echo $?` -ne 0 ]; then 11 | echo "no smartctl" 12 | exit 1 13 | fi 14 | 15 | checkSmartExitStatus() 16 | { 17 | STATUS=$1 18 | #if [ $(($STATUS & 1<<0)) -gt 0 ]; then echo "Command line did not parse"; fi 19 | # if [ $(($STATUS & 1<<1)) -gt 0 ]; then echo "Device open failed"; fi 20 | if [ $(($STATUS & 1<<2)) -gt 0 ]; then echo "Some command to the disk failed, or there was a checksum error in a SMART data structure"; fi 21 | if [ $(($STATUS & 1<<3)) -gt 0 ]; then echo "DISK FAILING"; fi 22 | if [ $(($STATUS & 1<<4)) -gt 0 ]; then echo "found prefail Attr <= threshold"; fi 23 | # if [ $(($STATUS & 1<<5)) -gt 0 ]; then echo "Some attributes have been <= threshold at some time in the past"; fi 24 | if [ $(($STATUS & 1<<7)) -gt 0 ]; then echo "self-test log contains records of errors"; fi 25 | } 26 | 27 | 28 | CACHE=`getFromCache "$ME" $TIMEOUT` 29 | if [ -z "$CACHE" ]; then 30 | 31 | lockf ${ME} 15 32 | [ $? -eq 0 ] || exit # unable to get lockfile 33 | DISKS="`sudo smartctl --scan-open`" 34 | if [ $? -ne 0 ]; then # old smartctl 35 | if [ `uname` = "Linux" ]; then 36 | DISKS="`ls -1 /dev/ | grep -E '^sd[a-z]$' 2>/dev/null | sed 's|^|/dev/|' | sed 's|$|,|'`" 37 | elif [ `uname` = "FreeBSD" ]; then 38 | DISKS="`ls -1 /dev/ | grep -E '^(ad[0-9]+|da[0-9]+|ada[0-9]+)$' 2>/dev/null | sed 's|^|/dev/|' | sed 's|$|,|'`" 39 | fi 40 | else 41 | DISKS="`echo \"$DISKS\" | sed 's|\ [\#\[].*|,|'`" 42 | fi 43 | OIFS="${IFS}" 44 | NIFS=$"," 45 | 46 | IFS="${NIFS}" 47 | 48 | for DISK in ${DISKS}; do 49 | IFS='${OIFS}' 50 | if [ -z "$DISK" ]; then 51 | continue 52 | fi 53 | DISK=${DISK%%\#*} 54 | DISK=${DISK%-*} 55 | DISK=`echo $DISK| xargs` 56 | sudo smartctl -q silent -a $DISK 2>/dev/null 57 | SMARTSTATUS=$? 58 | ERRLOG_COUNT=0 59 | if [ $SMARTSTATUS -ne 0 ]; then 60 | SMARTSTR=`checkSmartExitStatus \$SMARTSTATUS` 61 | if [ $((${SMARTSTATUS} & 1<<2)) -gt 0 ]; then 62 | sudo smartctl -a $DISK 2>/dev/null | grep -qE '(Vendor.*VMware|Vendor.*SUPER|Device.*DELL|device.*CD/DVD|Device.*processor|Device.*enclosure|Product.*Array|Virtual.*disk)' 63 | if [ $? -eq 0 ]; then 64 | continue 65 | fi 66 | sudo smartctl -i -A -l error -l selftest $DISK 2>/dev/null 1>/dev/null # try without health check 67 | if [ $? -eq 0 ]; then 68 | continue 69 | fi 70 | fi 71 | if [ $((${SMARTSTATUS} & 1<<6)) -gt 0 ]; then 72 | ERRLOG_COUNT="`sudo smartctl -l error $DISK 2>/dev/null | grep Error\ Count`" 73 | ERRLOG_COUNT=${ERRLOG_COUNT##*: } 74 | ERRLOG_COUNT=${ERRLOG_COUNT%% *} 75 | fi 76 | if [ -n "${SMARTSTR}" -o \( ${ERRLOG_COUNT} -gt ${ERRLOG_COUNT_THRESHOLD} \) ]; then 77 | SMARTSTR="ton of errors in log" 78 | RES="${RES}${DISK} ${SMARTSTR} 79 | " 80 | fi 81 | fi 82 | IFS="${NIFS}" 83 | done 84 | IFS="${OIFS}" 85 | if [ -z "$RES" ]; then 86 | RES="OK" 87 | fi 88 | 89 | writeToCache "$ME" "$RES" 90 | unlockf $ME 91 | else 92 | RES=${CACHE} 93 | fi 94 | 95 | echo "$RES" | tr -s "\n\n" "\n" 96 | -------------------------------------------------------------------------------- /zabbix/examples/zabbix_screen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import sys, os 4 | from zabbix_api import ZabbixAPI 5 | 6 | from argparse import ArgumentParser 7 | 8 | debug_flag = False 9 | progname = os.path.basename(sys.argv[0]) 10 | 11 | def error(msg) : 12 | sys.stderr.write('%s:%s\n' % (progname, msg)) 13 | sys.exit(255) 14 | 15 | def debug(msg) : 16 | if debug_flag : 17 | sys.stderr.write('%s:DEBUG:%s\n' % (progname, msg)) 18 | 19 | parser = ArgumentParser(description = 'Create Zabbix Screen with specified criteria') 20 | parser.add_argument('--url', dest = 'url', default = 'http://localhost/zabbix', help = 'Zabbix server address') 21 | parser.add_argument('-u', '--user', dest = 'user', default = 'admin', help = 'Zabbix user') 22 | parser.add_argument('-p', '--password', dest = 'password', default = '', help = 'Zabbix password') 23 | 24 | parser.add_argument('-S', '--screen', dest = 'screen', required = True, help = 'Screen name') 25 | parser.add_argument('-U', '--update', dest = 'update', default = False, action = 'store_true', help = 'Screen name') 26 | 27 | # if None, calculate from found items 28 | parser.add_argument('-H', dest = 'hsize', type = int, default = 2, help = 'Horizontal size of screen') 29 | 30 | parser.add_argument('--host', dest = 'host', default = None, help = '(Part of) Host to search for (either host or group must be spcified)') 31 | parser.add_argument('--group', dest = 'group', default = None, help = 'Group name to search for (either host or group must be spcified)') 32 | parser.add_argument('--graph', dest = 'graph', required = True, help = '(Part of) Graph name to search for') 33 | 34 | args = parser.parse_args() 35 | 36 | zapi = ZabbixAPI(server = args.url, path = "", log_level = 0) 37 | zapi.login(args.user, args.password) 38 | 39 | # Check if the screen is already exists 40 | 41 | screen = zapi.screen.get({'filter': {"name":args.screen}, 'selectScreenItems':'extend', 'output':'extend'}) 42 | 43 | debug('screen_result = %s' % (screen)) 44 | 45 | 46 | if screen and not args.update : 47 | error('Screen already exists') 48 | 49 | if screen : 50 | screen = screen[0] 51 | 52 | # Search for item and add to the screen 53 | host_list = [] 54 | if args.host : 55 | for host in zapi.host.get({'search':{'name':args.host}}) : 56 | host_list.append(host['hostid']) 57 | elif args.group : 58 | result = zapi.hostgroup.get({'filter':{'name': args.group}, 'output':'extend', 'selectHosts': 'extend'}) 59 | host_map = {} 60 | for r in result : 61 | for host in r['hosts'] : 62 | host_map[host['hostid']] = host['hostid'] 63 | host_list = host_map.values() 64 | 65 | debug('Host matches criteria = %s' % str(host_list)) 66 | 67 | # Look for graph item 68 | 69 | if host_list : 70 | result = zapi.graph.get({'hostids':host_list, 'search':{'name':args.graph}, 'output':'extend'}) 71 | else : 72 | result = zapi.graph.get({'search':{'name':args.graph}, 'output':'extend'}) 73 | 74 | # Screen creation 75 | hsize = args.hsize 76 | 77 | if screen and int(screen['hsize']) != int(hsize) : 78 | error("Couldn't update screen, existing screen hsize = %s, request screen hsize = %s" % (screen['hsize'], hsize)) 79 | 80 | # calculate vsize 81 | num_item = len(result) 82 | if screen and screen['screenitems'] : 83 | num_item += len(screen['screenitems']) 84 | vsize = num_item / hsize 85 | if num_item % hsize != 0 : 86 | vsize += 1 87 | 88 | debug('calculated hsize = %d, vsize = %d' % (hsize, vsize)) 89 | 90 | hpos = 0 91 | vpos = 0 92 | if screen : 93 | for i in screen['screenitems'] : 94 | if hpos < int(i['x']) : 95 | hpos = int(i['x']) 96 | if vpos < int(i['y']) : 97 | vpos = int(i['y']) 98 | 99 | if hpos >= (hsize - 1) : 100 | hpos = 0 101 | vpos += 1 102 | 103 | screen_items = [] 104 | 105 | for graph in result : 106 | data = {'colspan': 1, 107 | 'rowspan': 1, 108 | 'resourcetype': 0, 109 | 'resourceid': graph['graphid'], 110 | 'x': hpos, 111 | 'y': vpos, 112 | 'width': 500, 113 | 'height': 100, 114 | } 115 | if screen : 116 | data['screenid'] = screen['screenid'] 117 | screen_items.append(data) 118 | hpos += 1 119 | if hpos >= hsize : 120 | hpos = 0 121 | vpos += 1 122 | 123 | if debug_flag : 124 | for i in screen_items : 125 | debug('item = %s' % i) 126 | 127 | if screen : 128 | zapi.screen.update({'screenid': screen['screenid'], 'hsize': hsize, 'vsize': vsize}) 129 | for i in screen_items : 130 | zapi.screenitem.create(i) 131 | 132 | else : 133 | # Create the screen 134 | # need to know number of item first 135 | screen_creation_result = zapi.screen.create({'name': args.screen, 'hsize': hsize, 'vsize':vsize, 'screenitems': screen_items}) 136 | 137 | debug('Screen creation result = %s' % screen_creation_result) 138 | 139 | -------------------------------------------------------------------------------- /zabbix/zabbix_api.py: -------------------------------------------------------------------------------- 1 | # This is a port of the ruby zabbix api found here: 2 | # http://trac.red-tux.net/browser/ruby/api/zbx_api.rb 3 | # 4 | # LGPL 2.1 http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html 5 | # Zabbix API Python Library. 6 | # Original Ruby Library is Copyright (C) 2009 Andrew Nelson nelsonab(at)red-tux(dot)net 7 | # Python Library is Copyright (C) 2009 Brett Lentz brett.lentz(at)gmail(dot)com 8 | # 9 | # This library is free software; you can redistribute it and/or 10 | # modify it under the terms of the GNU Lesser General Public 11 | # License as published by the Free Software Foundation; either 12 | # version 2.1 of the License, or (at your option) any later version. 13 | # 14 | # This library is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | # Lesser General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU Lesser General Public 20 | # License along with this library; if not, write to the Free Software 21 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 22 | 23 | 24 | # NOTES: 25 | # The API requires zabbix 1.8 or later. 26 | # Currently, not all of the API is implemented, and some functionality is 27 | # broken. This is a work in progress. 28 | 29 | import base64 30 | import hashlib 31 | import logging 32 | import string 33 | import sys 34 | import ssl 35 | import socket 36 | try: 37 | import urllib2 38 | except ImportError: 39 | import urllib.request as urllib2 # python3 40 | import re 41 | from collections import deque 42 | 43 | try: 44 | from ssl import _create_unverified_context 45 | HAS_SSLCONTEXT = True 46 | except ImportError: 47 | HAS_SSLCONTEXT = False 48 | 49 | 50 | default_log_handler = logging.StreamHandler(sys.stdout) 51 | __logger = logging.getLogger("zabbix_api") 52 | __logger.addHandler(default_log_handler) 53 | __logger.log(10, "Starting logging") 54 | 55 | try: 56 | # Separate module or Python <2.6 57 | import simplejson as json 58 | __logger.log(15, "Using simplejson library") 59 | except ImportError: 60 | # Python >=2.6 61 | import json 62 | __logger.log(15, "Using native json library") 63 | 64 | 65 | def checkauth(fn): 66 | """ Decorator to check authentication of the decorated method """ 67 | 68 | def ret(self, *args): 69 | self.__checkauth__() 70 | return fn(self, args) 71 | return ret 72 | 73 | 74 | def dojson(fn): 75 | def wrapper(self, method, opts): 76 | self.logger.log(logging.DEBUG, 77 | "Going to do_request for %s with opts %s" 78 | % (repr(fn), repr(opts))) 79 | return self.do_request(self.json_obj(method, opts))['result'] 80 | return wrapper 81 | 82 | 83 | def version_compare(v1, v2): 84 | """ 85 | The result is 0 if v1 == v2, -1 if v1 < v2, and +1 if v1 > v2 86 | """ 87 | for v1_part, v2_part in zip(v1.split("."), v2.split(".")): 88 | if v1_part.isdecimal() and v2_part.isdecimal(): 89 | if int(v1_part) > int(v2_part): 90 | return 1 91 | elif int(v1_part) < int(v2_part): 92 | return -1 93 | else: 94 | if v1 > v2: 95 | return 1 96 | elif v1 < v2: 97 | return -1 98 | return 0 99 | 100 | 101 | class ZabbixAPIException(Exception): 102 | 103 | """ generic zabbix api exception 104 | code list: 105 | -32602 - Invalid params (eg already exists) 106 | -32500 - no permissions 107 | """ 108 | pass 109 | 110 | 111 | class Already_Exists(ZabbixAPIException): 112 | pass 113 | 114 | 115 | class InvalidProtoError(ZabbixAPIException): 116 | 117 | """ Recived an invalid proto """ 118 | pass 119 | 120 | 121 | class APITimeout(ZabbixAPIException): 122 | pass 123 | 124 | 125 | class ZabbixAPI(object): 126 | __username__ = '' 127 | __password__ = '' 128 | __tokenauth__ = False 129 | 130 | auth = '' 131 | url = '/api_jsonrpc.php' 132 | params = None 133 | method = None 134 | # HTTP or HTTPS 135 | proto = 'http' 136 | # HTTP authentication 137 | httpuser = None 138 | httppasswd = None 139 | timeout = 10 140 | validate_certs = None 141 | # sub-class instances. 142 | # Constructor Params: 143 | # server: Server to connect to 144 | # path: Path leading to the zabbix install 145 | # proto: Protocol to use. http or https 146 | # We're going to use proto://server/path to find the JSON-RPC api. 147 | # 148 | # user: HTTP auth username 149 | # passwd: HTTP auth password 150 | # log_level: logging level 151 | # r_query_len: max len query history 152 | # **kwargs: Data to pass to each api module 153 | 154 | def __init__(self, server='http://localhost/zabbix', user=httpuser, passwd=httppasswd, 155 | log_level=logging.WARNING, timeout=10, r_query_len=10, validate_certs=True, **kwargs): 156 | """ Create an API object. """ 157 | self._setuplogging() 158 | self.set_log_level(log_level) 159 | self.server = server 160 | self.url = server + '/api_jsonrpc.php' 161 | self.proto = self.server.split("://")[0] 162 | # self.proto=proto 163 | self.httpuser = user 164 | self.httppasswd = passwd 165 | self.timeout = timeout 166 | self.kwargs = kwargs 167 | self.id = 0 168 | self.r_query = deque([], maxlen=r_query_len) 169 | self.validate_certs = validate_certs 170 | self.debug(logging.INFO, "url: " + self.url) 171 | 172 | def _setuplogging(self): 173 | self.logger = logging.getLogger("zabbix_api.%s" % self.__class__.__name__) 174 | 175 | def set_log_level(self, level): 176 | self.debug(logging.INFO, "Set logging level to %d" % level) 177 | self.logger.setLevel(level) 178 | 179 | def recent_query(self): 180 | """ 181 | return recent query 182 | """ 183 | return list(self.r_query) 184 | 185 | def debug(self, level, var="", msg=None): 186 | strval = str(level) + ": " 187 | if msg: 188 | strval = strval + str(msg) 189 | if var != "": 190 | strval = strval + str(var) 191 | 192 | self.logger.log(level, strval) 193 | 194 | def json_obj(self, method, params={}, auth=True): 195 | obj = {'jsonrpc': '2.0', 196 | 'method': method, 197 | 'params': params, 198 | 'auth': self.auth, 199 | 'id': self.id} 200 | if not auth: 201 | del obj['auth'] 202 | 203 | self.debug(logging.DEBUG, "json_obj: " + str(obj)) 204 | 205 | return json.dumps(obj) 206 | 207 | def login(self, user='', password='', save=True, api_token=None): 208 | if api_token is not None: 209 | # due to ZBX-21688 we are unable to check if the token is valid 210 | # obj = self.json_obj('user.checkAuthentication', {'sessionid': api_token}, auth=False) 211 | # result = self.do_request(obj) 212 | self.debug(logging.DEBUG, "Using API Token for auth") 213 | self.auth=api_token 214 | self.__tokenauth__ = True 215 | return 216 | 217 | if user != '': 218 | l_user = user 219 | l_password = password 220 | 221 | if save: 222 | self.__username__ = user 223 | self.__password__ = password 224 | elif self.__username__ != '': 225 | l_user = self.__username__ 226 | l_password = self.__password__ 227 | else: 228 | raise ZabbixAPIException("No authentication information available.") 229 | 230 | # don't print the raw password. 231 | hashed_pw_string = "md5(" + hashlib.md5(l_password.encode('utf-8')).hexdigest() + ")" 232 | self.debug(logging.DEBUG, "Trying to login with %s:%s" % 233 | (repr(l_user), repr(hashed_pw_string))) 234 | if version_compare(self.api_version(), '5.4') >= 0: 235 | login_arg = {'username': l_user, 'password': l_password} 236 | else: 237 | login_arg = {'user': l_user, 'password': l_password} 238 | obj = self.json_obj('user.login', login_arg, auth=False) 239 | result = self.do_request(obj) 240 | self.auth = result['result'] 241 | 242 | def logout(self): 243 | if self.__tokenauth__: 244 | # Do nothing for logout for API tokens. 245 | self.debug(logging.DEBUG, "Clearing auth information due to use of API Token") 246 | self.auth = '' 247 | self.__username__ = '' 248 | self.__password__ = '' 249 | self.__tokenauth__ = False 250 | return 251 | if self.auth == '': 252 | raise ZabbixAPIException("No authentication information available.") 253 | self.debug(logging.DEBUG, "Trying to logout user: %s." % self.__username__) 254 | obj = self.json_obj('user.logout', auth=True) 255 | result = self.do_request(obj) 256 | if result['result']: 257 | self.auth = '' 258 | self.__username__ = '' 259 | self.__password__ = '' 260 | 261 | def test_login(self): 262 | if self.auth != '': 263 | obj = self.json_obj('user.checkAuthentication', {'sessionid': self.auth}) 264 | result = self.do_request(obj) 265 | 266 | if not result['result']: 267 | self.auth = '' 268 | return False # auth hash bad 269 | return True # auth hash good 270 | else: 271 | return False 272 | 273 | def do_request(self, json_obj): 274 | headers = {'Content-Type': 'application/json-rpc', 275 | 'User-Agent': 'python/zabbix_api'} 276 | 277 | if self.httpuser: 278 | self.debug(logging.INFO, "HTTP Auth enabled") 279 | credentials = (self.httpuser + ':' + self.httppasswd).encode('ascii') 280 | auth = 'Basic ' + base64.b64encode(credentials).decode("ascii") 281 | headers['Authorization'] = auth 282 | self.r_query.append(str(json_obj)) 283 | self.debug(logging.INFO, "Sending: " + str(json_obj)) 284 | self.debug(logging.DEBUG, "Sending headers: " + str(headers)) 285 | 286 | request = urllib2.Request(url=self.url, data=json_obj.encode('utf-8'), headers=headers) 287 | if self.proto == "https": 288 | if HAS_SSLCONTEXT and not self.validate_certs: 289 | https_handler = urllib2.HTTPSHandler(debuglevel=0, context=_create_unverified_context()) 290 | else: 291 | https_handler = urllib2.HTTPSHandler(debuglevel=0) 292 | opener = urllib2.build_opener(https_handler) 293 | elif self.proto == "http": 294 | http_handler = urllib2.HTTPHandler(debuglevel=0) 295 | opener = urllib2.build_opener(http_handler) 296 | else: 297 | raise ZabbixAPIException("Unknow protocol %s" % self.proto) 298 | 299 | urllib2.install_opener(opener) 300 | try: 301 | response = opener.open(request, timeout=self.timeout) 302 | except ssl.SSLError as e: 303 | if hasattr(e, 'message'): 304 | e = e.message 305 | raise ZabbixAPIException("ssl.SSLError - %s" % e) 306 | except socket.timeout as e: 307 | raise APITimeout("HTTP read timeout",) 308 | except urllib2.URLError as e: 309 | if hasattr(e, 'message') and e.message: 310 | e = e.message 311 | elif hasattr(e, 'reason'): 312 | e = e.reason 313 | raise ZabbixAPIException("urllib2.URLError - %s" % e) 314 | self.debug(logging.INFO, "Response Code: " + str(response.code)) 315 | 316 | # NOTE: Getting a 412 response code means the headers are not in the 317 | # list of allowed headers. 318 | if response.code != 200: 319 | raise ZabbixAPIException("HTTP ERROR %s: %s" 320 | % (response.status, response.reason)) 321 | reads = response.read() 322 | if len(reads) == 0: 323 | raise ZabbixAPIException("Received zero answer") 324 | try: 325 | jobj = json.loads(reads.decode('utf-8')) 326 | except ValueError as msg: 327 | print ("unable to decode. returned string: %s" % reads) 328 | sys.exit(-1) 329 | self.debug(logging.DEBUG, "Response Body: " + str(jobj)) 330 | 331 | self.id += 1 332 | 333 | if 'error' in jobj: # some exception 334 | msg = "Error %s: %s, %s while sending %s" % (jobj['error']['code'], 335 | jobj['error']['message'], jobj['error']['data'], str(json_obj)) 336 | if re.search(".*already\sexists.*", jobj["error"]["data"], re.I): # already exists 337 | raise Already_Exists(msg, jobj['error']['code']) 338 | else: 339 | raise ZabbixAPIException(msg, jobj['error']['code']) 340 | return jobj 341 | 342 | def logged_in(self): 343 | if self.auth != '': 344 | return True 345 | return False 346 | 347 | def api_version(self, **options): 348 | obj = self.do_request(self.json_obj('apiinfo.version', options, auth=False)) 349 | return obj['result'] 350 | 351 | def __checkauth__(self): 352 | if not self.logged_in(): 353 | raise ZabbixAPIException("Not logged in.") 354 | 355 | def __getattr__(self, name): 356 | return ZabbixAPISubClass(self, dict({"prefix": name}, **self.kwargs)) 357 | 358 | 359 | class ZabbixAPISubClass(ZabbixAPI): 360 | 361 | """ wrapper class to ensure all calls go through the parent object """ 362 | parent = None 363 | data = None 364 | 365 | def __init__(self, parent, data, **kwargs): 366 | self._setuplogging() 367 | self.debug(logging.INFO, "Creating %s" % self.__class__.__name__) 368 | self.data = data 369 | self.parent = parent 370 | 371 | # Save any extra info passed in 372 | for key, val in kwargs.items(): 373 | setattr(self, key, val) 374 | self.debug(logging.WARNING, "Set %s:%s" % (repr(key), repr(val))) 375 | 376 | def __getattr__(self, name): 377 | if self.data["prefix"] == "configuration" and name == "import_": # workaround for "import" method 378 | name = "import" 379 | 380 | def method(*opts): 381 | return self.universal("%s.%s" % (self.data["prefix"], name), opts[0]) 382 | return method 383 | 384 | def __checkauth__(self): 385 | self.parent.__checkauth__() 386 | 387 | def do_request(self, req): 388 | return self.parent.do_request(req) 389 | 390 | def json_obj(self, method, param): 391 | return self.parent.json_obj(method, param) 392 | 393 | @dojson 394 | @checkauth 395 | def universal(self, **opts): 396 | return opts 397 | --------------------------------------------------------------------------------