1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
|
#
# Most of the code in this file is from the project PyZabbix:
# https://github.com/lukecyca/pyzabbix
#
# We have modified the login method to be able to send an auth-token so
# we do not have to login again as long as the auth-token used is still
# active.
#
# We have also modified the output when an error happens to not show
# the username + password information.
#
from __future__ import unicode_literals
import logging
import requests
import json
class _NullHandler(logging.Handler):
def emit(self, record):
pass
logger = logging.getLogger(__name__)
logger.addHandler(_NullHandler())
class ZabbixAPIException(Exception):
""" generic zabbix api exception
code list:
-32602 - Invalid params (eg already exists)
-32500 - no permissions
"""
pass
class ZabbixAPI(object):
def __init__(self,
server='http://localhost/zabbix',
session=None,
use_authenticate=False,
timeout=None):
"""
Parameters:
server: Base URI for zabbix web interface (omitting /api_jsonrpc.php)
session: optional pre-configured requests.Session instance
use_authenticate: Use old (Zabbix 1.8) style authentication
timeout: optional connect and read timeout in seconds, default: None (if you're using Requests >= 2.4 you can set it as tuple: "(connect, read)" which is used to set individual connect and read timeouts.)
"""
if session:
self.session = session
else:
self.session = requests.Session()
# Default headers for all requests
self.session.headers.update({
'Content-Type': 'application/json-rpc',
'User-Agent': 'python/pyzabbix',
'Cache-Control': 'no-cache'
})
self.use_authenticate = use_authenticate
self.auth = ''
self.id = 0
self.timeout = timeout
self.url = server + '/api_jsonrpc.php'
logger.info("JSON-RPC Server Endpoint: %s", self.url)
def login(self, user='', password='', auth_token=''):
"""
Convenience method for calling user.authenticate and storing the
resulting auth token for further commands. If
use_authenticate is set, it uses the older (Zabbix 1.8)
authentication command
"""
# If the file $HOME/.zabbix-cli_auth_token exists from an
# older session, the system will try to reuse the
# API-auth-token saved in this file.
#
# If the file $HOME/.zabbix-cli_auth_token does not exist, we
# will login with the username and password.
#
if auth_token == '':
self.auth = ''
if self.use_authenticate:
self.auth = self.user.authenticate(user=user, password=password)
else:
self.auth = self.user.login(user=user, password=password)
else:
self.auth = auth_token
self.api_version()
return self.auth
def confimport(self, format='', source='', rules=''):
"""Alias for configuration.import because it clashes with
Python's import reserved keyword"""
return self.do_request(
method="configuration.import",
params={"format": format, "source": source, "rules": rules}
)['result']
def api_version(self):
return self.apiinfo.version()
def do_request(self, method, params=None):
request_json = {
'jsonrpc': '2.0',
'method': method,
'params': params or {},
'id': self.id,
}
# We don't have to pass the auth token if asking for the apiinfo.version
if self.auth and method != 'apiinfo.version':
request_json['auth'] = self.auth
logger.debug("Sending: %s", json.dumps(request_json,
indent=4,
separators=(',', ': ')))
response = self.session.post(
self.url,
data=json.dumps(request_json),
timeout=self.timeout
)
logger.debug("Response Code: %s", str(response.status_code))
# NOTE: Getting a 412 response code means the headers are not in the
# list of allowed headers.
response.raise_for_status()
if not len(response.text):
raise ZabbixAPIException("Received empty response")
try:
response_json = json.loads(response.text)
except ValueError:
raise ZabbixAPIException(
"Unable to parse json: %s" % response.text
)
logger.debug("Response Body: %s", json.dumps(response_json,
indent=4,
separators=(',', ': ')))
self.id += 1
if 'error' in response_json: # some exception
if 'data' not in response_json['error']: # some errors don't contain 'data': workaround for ZBX-9340
response_json['error']['data'] = "No data"
#
# We do not want to get the password value in the error
# message if the user uses a not valid username or
# password.
#
if response_json['error']['data'] == 'Login name or password is incorrect.':
msg = "Error {code}: {message}: {data}".format(
code=response_json['error']['code'],
message=response_json['error']['message'],
data=response_json['error']['data'])
elif response_json['error']['data'] == 'Not authorized':
msg = "Error {code}: {data}: {message}".format(
code=response_json['error']['code'],
data=response_json['error']['data'],
message=response_json['error']['message'] + '\n\n* Your API-auth-token has probably expired.\n' +
'* Try to login again with your username and password')
else:
msg = "Error {code}: {message}: {data} while sending {json}".format(
code=response_json['error']['code'],
message=response_json['error']['message'],
data=response_json['error']['data'],
json=str(request_json))
raise ZabbixAPIException(msg, response_json['error']['code'])
return response_json
def __getattr__(self, attr):
"""Dynamically create an object class (ie: host)"""
return ZabbixAPIObjectClass(attr, self)
class ZabbixAPIObjectClass(object):
def __init__(self, name, parent):
self.name = name
self.parent = parent
def __getattr__(self, attr):
"""Dynamically create a method (ie: get)"""
def fn(*args, **kwargs):
if args and kwargs:
raise TypeError("Found both args and kwargs")
return self.parent.do_request(
'{0}.{1}'.format(self.name, attr),
args or kwargs
)['result']
return fn
|