From 654ed5857ba563c2ad16a9fb37df297eed8af65e Mon Sep 17 00:00:00 2001 From: v-zhuravlev Date: Sat, 23 Feb 2019 01:11:54 +0300 Subject: [PATCH 01/15] updated .gitignore --- .gitignore | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/.gitignore b/.gitignore index bed9c27..84bc4ca 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,70 @@ *~ +# editors +.vscode +.idea + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + + +# pyenv +.python-version + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ From 338cfd1ccae486a0e465bdd9b45cc1e9df603c0b Mon Sep 17 00:00:00 2001 From: v-zhuravlev Date: Fri, 1 Mar 2019 02:10:10 +0300 Subject: [PATCH 02/15] added python3.7 to travis --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 44149eb..18ea8d6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,5 @@ +dist: xenial + sudo: required services: @@ -10,6 +12,7 @@ python: - "3.4" - "3.5" - "3.6" + - "3.7" env: - ZABBIX_VERSION: 2 From c1fdec3ee15881f89ff3f1b3c95d82730e454762 Mon Sep 17 00:00:00 2001 From: v-zhuravlev Date: Sun, 31 Mar 2019 23:00:14 +0300 Subject: [PATCH 03/15] filter to hide password and auth_token in logs and exceptions --- README.rst | 39 ++++++++++++---- pyzabbix/api.py | 6 ++- pyzabbix/logger.py | 47 ++++++++++++++++++- tests/test_api.py | 72 +++++++++++++++++++++++++++-- tests/test_hide_sensitive_filter.py | 36 +++++++++++++++ 5 files changed, 184 insertions(+), 16 deletions(-) create mode 100644 tests/test_hide_sensitive_filter.py diff --git a/README.rst b/README.rst index 11419de..6f06916 100644 --- a/README.rst +++ b/README.rst @@ -57,16 +57,35 @@ Or use 'with' statement to logout automatically: # Get all monitored hosts result1 = zapi.host.get(monitored_hosts=1, output='extend') - # Get all disabled hosts - result2 = zapi.do_request('host.get', - { - 'filter': {'status': 1}, - 'output': 'extend' - }) - - # Filter results - hostnames1 = [host['host'] for host in result1] - hostnames2 = [host['host'] for host in result2['result']] +Enable logging: + +.. code:: python + + import sys + import logging + from pyzabbix.api import ZabbixAPI + + # Create ZabbixAPI class instance + logger = logging.getLogger("pyzabbix") + logger.setLevel(logging.DEBUG) + handler = logging.StreamHandler(sys.stdout) + logger.addHandler(handler) + + zapi = ZabbixAPI(url='http://localhost', user='Admin', password='zabbix') + +Note that passwords and auth tokens are hidden when raw messages are logged or raised in exceptions ( but not hidden if print() is used): + +.. code:: python + + ZabbixAPI.login(Admin,********) + Call user.login method + urllib2.Request(http://localhost/api_jsonrpc.php, {"jsonrpc": "2.0", "method": "user.login", "params": {"user": "Admin", "password": "********"}, "id": "1"}) + Response Body: { + "jsonrpc": "2.0", + "result": "********", + "id": "1" + } + ZabbixSender ~~~~~~~~~~~~ diff --git a/pyzabbix/api.py b/pyzabbix/api.py index 1fd4c21..c84a96b 100644 --- a/pyzabbix/api.py +++ b/pyzabbix/api.py @@ -31,11 +31,12 @@ # the urllib.request. import urllib.request as urllib2 -from .logger import NullHandler +from .logger import NullHandler, HideSensitiveFilter, HideSensitiveService null_handler = NullHandler() logger = logging.getLogger(__name__) logger.addHandler(null_handler) +logger.addFilter(HideSensitiveFilter()) class ZabbixAPIException(Exception): @@ -49,6 +50,7 @@ def __init__(self, *args): super(Exception, self).__init__(*args) if len(args) == 1 and isinstance(args[0], dict): self.error = args[0] + self.error['json'] = HideSensitiveService.hide_sensitive(self.error['json']) self.message = self.error['message'] self.code = self.error['code'] self.data = self.error['data'] @@ -191,7 +193,7 @@ def _login(self, user='', password=''): :param password: Zabbix user password """ - logger.debug("ZabbixAPI.login({0},{1})".format(user, password)) + logger.debug("ZabbixAPI.login({0},{1})".format(user, HideSensitiveService.HIDEMASK)) self.auth = None diff --git a/pyzabbix/logger.py b/pyzabbix/logger.py index 15a1043..da834a5 100644 --- a/pyzabbix/logger.py +++ b/pyzabbix/logger.py @@ -16,8 +16,8 @@ # # You should have received a copy of the GNU General Public License # along with py-zabbix. If not, see . - import logging +import re class NullHandler(logging.Handler): @@ -28,3 +28,48 @@ class NullHandler(logging.Handler): def emit(self, record): pass + + +class HideSensitiveFilter(logging.Filter): + """Filter to hide sensitive Zabbix info (password, auth) in logs""" + + def __init__(self, *args, **kwargs): + super(logging.Filter, self).__init__(*args, **kwargs) + self.hide_sensitive = HideSensitiveService.hide_sensitive + + def filter(self, record): + + record.msg = self.hide_sensitive(record.msg) + if record.args: + newargs = [self.hide_sensitive(arg) if isinstance(arg, str) + else arg for arg in record.args] + record.args = tuple(newargs) + + return 1 + + +class HideSensitiveService(object): + """ + Service to hide sensitive Zabbix info (password, auth tokens) + Call classmethod hide_sensitive(message: str) + """ + + HIDEMASK = "********" + _pattern = re.compile( + r'(?Ppassword)["\']\s*:\s*u?["\'](?P.+?)["\']' + r'|' + r'\W(?P[a-z0-9]{32})') + + @classmethod + def hide_sensitive(cls, message): + def hide(m): + if m.group('key') == 'password': + return m.string[m.start():m.end()].replace( + m.group('password'), cls.HIDEMASK) + else: + return m.string[m.start():m.end()].replace( + m.group('token'), cls.HIDEMASK) + + message = re.sub(cls._pattern, hide, message) + + return message diff --git a/tests/test_api.py b/tests/test_api.py index 5174b83..2d2db3a 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -1,7 +1,8 @@ import json -from unittest import TestCase -from pyzabbix import ZabbixAPI, ssl_context_compat +import unittest +from pyzabbix import ZabbixAPI, ZabbixAPIException, ssl_context_compat +from pyzabbix.logger import HideSensitiveService try: from mock import patch except ImportError: @@ -32,7 +33,7 @@ def getcode(self): return self.code -class TestZabbixAPI(TestCase): +class TestZabbixAPI(unittest.TestCase): def setUp(self): "Mock urllib2.urlopen" @@ -121,5 +122,70 @@ def test_get_id_item(self): res = ZabbixAPI().get_id('item', item='Test Item') self.assertEqual(res, 23298) + @unittest.skipUnless(version_info >= (3, 4), + "Test not supported for python < 3.4") + def test_hide_sensitive_in_logger(self): + """Test that logger hides passwords and auth keys (python 3.4+)""" + + ret = { + 'jsonrpc': '2.0', + 'result': '0424bd59b807674191e7d77572075f33', + 'id': 1 + } + self.urlopen_mock.return_value = MockResponse(json.dumps(ret)) + + with self.assertLogs('pyzabbix', level='DEBUG') as cm: + + # Create ZabbixAPI class instance + zapi = ZabbixAPI(url='https://localhost/zabbix', + user='Admin', password='PASSWORD') + + ret = { + 'jsonrpc': '2.0', + 'result': + [{ + 'itemid': '23298', + 'hostid': '10084', + 'name': 'Test Item', + 'key_': 'system.cpu.switches', + 'description': '', + }], + 'id': 1, + } + self.urlopen_mock.return_value = MockResponse(json.dumps(ret)) + zapi.get_id('item', item='Test Item') + + log_string = "".join(cm.output) + + self.assertNotIn('PASSWORD', log_string) + self.assertNotIn('0424bd59b807674191e7d77572075f33', log_string) + + # count number or passwords/token replacements + # (including 'DEBUG:pyzabbix.api:ZabbixAPI.login(Admin,********)') + self.assertEqual(log_string.count(HideSensitiveService.HIDEMASK), 4) + + def test_hide_sensitive_in_exception(self): + """Test that exception raised hides passwords and auth keys""" + + with self.assertRaises(ZabbixAPIException) as cm: + res = { + 'code': -32602, + 'message': 'Invalid params', + 'data': 'Incorrect API "host2".', + 'json': """ + {'jsonrpc': '2.0', + 'method': 'host2.get', + 'params': {'monitored_hosts': 1, 'output': 'extend'}, + 'id': '1', + 'auth': '0424bd59b807674191e7d77572075f33'} + """ + } + raise ZabbixAPIException(res) + + self.assertNotIn("0424bd59b807674191e7d77572075f33", cm.exception.json) + self.assertEqual( + cm.exception.json.count(HideSensitiveService.HIDEMASK), + 1) + def tearDown(self): self.patcher.stop() diff --git a/tests/test_hide_sensitive_filter.py b/tests/test_hide_sensitive_filter.py new file mode 100644 index 0000000..d79edb1 --- /dev/null +++ b/tests/test_hide_sensitive_filter.py @@ -0,0 +1,36 @@ +import unittest +from pyzabbix.logger import HideSensitiveService + + +class TestHideSensitiveFilter(unittest.TestCase): + + def test_hide_filter_multi(self): + + test_message = '"password": "hide_this", ' \ + '"result": "0424bd59b807674191e7d77572075f33", ' \ + '"result": "do_not_hide_this", ' \ + '"auth": "0424bd59b807674191e7d77572075f33"' + expected = ('"password": "{}", ' + '"result": "{}", ' + '"result": "do_not_hide_this", ' + '"auth": "{}"').format( + HideSensitiveService.HIDEMASK, + HideSensitiveService.HIDEMASK, + HideSensitiveService.HIDEMASK) + + self.assertEquals(HideSensitiveService.hide_sensitive(test_message), + expected) + + def test_hide_filter_do_not_change_url(self): + + # Filter should not hide 'zabbix' in URL: + # https://localhost/zabbix/api_jsonrpc.php + test = 'urllib2.Request(https://localhost/zabbix/api_jsonrpc.php,' \ + '{ ... "params": {"user": "Admin", "password": "zabbix"}, '\ + '"id": "1"}))' + expected = 'urllib2.Request(https://localhost/zabbix/api_jsonrpc.php,'\ + '{ ... "params": {"user": "Admin", "password": "' + \ + HideSensitiveService.HIDEMASK + '"}, "id": "1"}))' + + self.assertEqual(HideSensitiveService.hide_sensitive(test), + expected) From badd6b92ac8841d473fc441ce67bc8a90102094a Mon Sep 17 00:00:00 2001 From: v-zhuravlev Date: Sun, 31 Mar 2019 23:20:16 +0300 Subject: [PATCH 04/15] removed deprecation warning (warn == warning) --- pyzabbix/sender.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyzabbix/sender.py b/pyzabbix/sender.py index c356c4a..8fa4b04 100644 --- a/pyzabbix/sender.py +++ b/pyzabbix/sender.py @@ -408,7 +408,7 @@ def _chunk_send(self, metrics): except Exception as err: # In case of error we should close connection, otherwise # we will close it after data will be received. - logger.warn('Sending failed: %s', getattr(err, 'msg', str(err))) + logger.warning('Sending failed: %s', getattr(err, 'msg', str(err))) connection.close() raise Exception(err) From ac4b6dd621235ef7a6756ab88347e2d517cafe76 Mon Sep 17 00:00:00 2001 From: v-zhuravlev Date: Sun, 31 Mar 2019 23:24:50 +0300 Subject: [PATCH 05/15] removed deprecation warning --- tests/test_hide_sensitive_filter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_hide_sensitive_filter.py b/tests/test_hide_sensitive_filter.py index d79edb1..a54e28b 100644 --- a/tests/test_hide_sensitive_filter.py +++ b/tests/test_hide_sensitive_filter.py @@ -18,8 +18,8 @@ def test_hide_filter_multi(self): HideSensitiveService.HIDEMASK, HideSensitiveService.HIDEMASK) - self.assertEquals(HideSensitiveService.hide_sensitive(test_message), - expected) + self.assertEqual(HideSensitiveService.hide_sensitive(test_message), + expected) def test_hide_filter_do_not_change_url(self): From bb4c316a5805b27e85ffedce50ac3c672b5453d2 Mon Sep 17 00:00:00 2001 From: jbskytap <41307692+jbskytap@users.noreply.github.com> Date: Thu, 2 May 2019 14:00:31 -0700 Subject: [PATCH 06/15] docs - typo --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 11419de..382d905 100644 --- a/README.rst +++ b/README.rst @@ -12,7 +12,7 @@ You can install Zabbix modules for Python with pip: pip install py-zabbix -Official documentaion for `py-zabbix `__ +Official documentation for `py-zabbix `__ -------------------------------------------------------------------------------------- Examples From e1f80e4c5d5648a77487b502bcdd4be87957f8d3 Mon Sep 17 00:00:00 2001 From: Boris HUISGEN Date: Wed, 5 Jun 2019 22:21:33 +0200 Subject: [PATCH 07/15] Use socket exceptions instead of bare except Signed-off-by: Boris HUISGEN --- pyzabbix/sender.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pyzabbix/sender.py b/pyzabbix/sender.py index c356c4a..217d8af 100644 --- a/pyzabbix/sender.py +++ b/pyzabbix/sender.py @@ -365,7 +365,7 @@ def _get_response(self, connection): try: connection.close() - except Exception as err: + except socket.error: pass return result @@ -405,19 +405,19 @@ def _chunk_send(self, metrics): '%d seconds', host_addr, self.timeout) connection.close() raise socket.timeout - except Exception as err: + except socket.error as err: # In case of error we should close connection, otherwise # we will close it after data will be received. logger.warn('Sending failed: %s', getattr(err, 'msg', str(err))) connection.close() - raise Exception(err) + raise err response = self._get_response(connection) logger.debug('%s response: %s', host_addr, response) if response and response.get('response') != 'success': logger.debug('Response error: %s}', response) - raise Exception(response) + raise socket.error(response) return response From d3ca96e0cb922636785f83f89d90a865f2e63bb6 Mon Sep 17 00:00:00 2001 From: Boris HUISGEN Date: Thu, 6 Jun 2019 19:54:21 +0200 Subject: [PATCH 08/15] Update tests Signed-off-by: Boris HUISGEN --- tests/test_sender.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/test_sender.py b/tests/test_sender.py index 32962ba..ca2775c 100644 --- a/tests/test_sender.py +++ b/tests/test_sender.py @@ -1,5 +1,6 @@ import json import os +import socket import struct @@ -179,7 +180,7 @@ def test_get_response_fail(self, mock_socket): @patch('pyzabbix.sender.socket.socket', autospec=autospec) def test_get_response_fail_s_close(self, mock_socket): mock_socket.recv.side_effect = (b'IDDQD', self.resp_body) - mock_socket.close.side_effect = Exception + mock_socket.close.side_effect = socket.error zs = ZabbixSender() result = zs._get_response(mock_socket) @@ -202,11 +203,11 @@ def test_send(self, mock_socket): @patch('pyzabbix.sender.socket.socket', autospec=autospec) def test_send_sendall_exception(self, mock_socket): mock_socket.return_value = mock_socket - mock_socket.sendall.side_effect = Exception + mock_socket.sendall.side_effect = socket.error zm = ZabbixMetric('host1', 'key1', 100500, 1457358608) zs = ZabbixSender() - with self.assertRaises(Exception): + with self.assertRaises(socket.error): zs.send([zm]) @patch('pyzabbix.sender.socket.socket', autospec=autospec) @@ -220,5 +221,5 @@ def test_send_failed(self, mock_socket): zm = ZabbixMetric('host1', 'key1', 100500, 1457358608) zs = ZabbixSender() - with self.assertRaises(Exception): + with self.assertRaises(socket.error): zs.send([zm]) From 7075d75b9e364368a9401037bd40e4bd23b0f7b1 Mon Sep 17 00:00:00 2001 From: Boris HUISGEN Date: Thu, 6 Jun 2019 19:55:30 +0200 Subject: [PATCH 09/15] Use ValueError instead of Exception Signed-off-by: Boris HUISGEN --- pyzabbix/sender.py | 2 +- tests/test_sender.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyzabbix/sender.py b/pyzabbix/sender.py index 217d8af..d523313 100644 --- a/pyzabbix/sender.py +++ b/pyzabbix/sender.py @@ -121,7 +121,7 @@ def __init__(self, host, key, value, clock=None): if isinstance(clock, (float, int)): self.clock = int(clock) else: - raise Exception('Clock must be time in unixtime format') + raise ValueError('Clock must be time in unixtime format') def __repr__(self): """Represent detailed ZabbixMetric view.""" diff --git a/tests/test_sender.py b/tests/test_sender.py index ca2775c..e645850 100644 --- a/tests/test_sender.py +++ b/tests/test_sender.py @@ -43,7 +43,7 @@ def test_init(self): self.assertEqual(zm.clock, 1457358608) def test_init_err(self): - with self.assertRaises(Exception): + with self.assertRaises(ValueError): ZabbixMetric('host1', 'key1', 100500, '1457358608.01') def test_repr(self): From 83ec1b122deb3482eebda989fcadf6f96a82684c Mon Sep 17 00:00:00 2001 From: Brian Lloyd Date: Tue, 16 Jul 2019 13:54:05 -0600 Subject: [PATCH 10/15] Try opening a socket with AF_INET6, and then AF_INET --- pyzabbix/sender.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/pyzabbix/sender.py b/pyzabbix/sender.py index c356c4a..66328ac 100644 --- a/pyzabbix/sender.py +++ b/pyzabbix/sender.py @@ -387,8 +387,15 @@ def _chunk_send(self, metrics): for host_addr in self.zabbix_uri: logger.debug('Sending data to %s', host_addr) - # create socket object - connection_ = socket.socket() + try: + # IPv6 + connection_ = socket.socket(socket.AF_INET6) + except socket.error: + # IPv4 + try: + connection_ = socket.socket(socket.AF_INET) + except socket.error: + raise Exception("Error creating socket for {host_addr}".format(host_addr=host_addr)) if self.socket_wrapper: connection = self.socket_wrapper(connection_) else: From 145fe731d9db476b1816759d6ca5e6554201885c Mon Sep 17 00:00:00 2001 From: Brian Lloyd Date: Wed, 17 Jul 2019 10:31:17 -0600 Subject: [PATCH 11/15] Swap IPv4/IPv6 order so IPv4 is tried first. Remove trailing whitespace. --- pyzabbix/sender.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pyzabbix/sender.py b/pyzabbix/sender.py index 66328ac..e856c2c 100644 --- a/pyzabbix/sender.py +++ b/pyzabbix/sender.py @@ -388,12 +388,12 @@ def _chunk_send(self, metrics): logger.debug('Sending data to %s', host_addr) try: - # IPv6 - connection_ = socket.socket(socket.AF_INET6) - except socket.error: # IPv4 - try: - connection_ = socket.socket(socket.AF_INET) + connection_ = socket.socket(socket.AF_INET) + except socket.error: + # IPv6 + try: + connection_ = socket.socket(socket.AF_INET6) except socket.error: raise Exception("Error creating socket for {host_addr}".format(host_addr=host_addr)) if self.socket_wrapper: From 2b15897fcaeef800462ec34b2b6967a636d2d4f0 Mon Sep 17 00:00:00 2001 From: Amin Dandache Date: Thu, 23 Jan 2020 11:34:34 +0100 Subject: [PATCH 12/15] add basic_auth --- pyzabbix/api.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/pyzabbix/api.py b/pyzabbix/api.py index 1fd4c21..d16ff0c 100644 --- a/pyzabbix/api.py +++ b/pyzabbix/api.py @@ -22,6 +22,7 @@ import os import ssl import sys +import base64 # For Python 2 and 3 compatibility try: @@ -137,6 +138,9 @@ class ZabbixAPI(object): :param use_authenticate: Use `user.authenticate` method if `True` else `user.login`. + :type use_basic_auth: bool + :param use_basic_auth: Using basic auth if `True` + :type user: str :param user: Zabbix user name. Default: `ZABBIX_USER` or `'Admin'`. @@ -158,7 +162,7 @@ class ZabbixAPI(object): >>> z.do_request('host.getobjects', {'status': 1}) """ - def __init__(self, url=None, use_authenticate=False, user=None, + def __init__(self, url=None, use_authenticate=False, use_basic_auth=False, user=None, password=None): url = url or os.environ.get('ZABBIX_URL') or 'https://localhost/zabbix' @@ -166,8 +170,10 @@ def __init__(self, url=None, use_authenticate=False, user=None, password = password or os.environ.get('ZABBIX_PASSWORD') or 'zabbix' self.use_authenticate = use_authenticate + self.use_basic_auth = use_basic_auth self.auth = None self.url = url + '/api_jsonrpc.php' + self.base64_cred = self.cred_to_base64(user, password) if self.use_basic_auth else None self._login(user, password) logger.debug("JSON-PRC Server: %s", self.url) @@ -215,6 +221,19 @@ def __enter__(self): def __exit__(self, *args): self._logout() + @staticmethod + def cred_to_base64(user, password): + """Create header for basic authorization + :type user: str + :param user: Zabbix user + + :type password: str + :param password: Zabbix user password + :return: str + """ + base64string = base64.b64encode('{}:{}'.format(user, password).encode()) + return base64string.decode() + def api_version(self): """Return version of server Zabbix API. @@ -262,6 +281,9 @@ def do_request(self, method, params=None): req.get_method = lambda: 'POST' req.add_header('Content-Type', 'application/json-rpc') + if self.use_basic_auth: + req.add_header("Authorization", "Basic {}".format(self.base64_cred)) + try: res = urlopen(req) res_str = res.read().decode('utf-8') From c72fdd9af13eed96cd5832c31644e2ae94fb67b1 Mon Sep 17 00:00:00 2001 From: Alexey Dubkov Date: Tue, 28 Apr 2020 23:03:40 -0700 Subject: [PATCH 13/15] ad-header-user-agent as py-zabbix/<> --- pyzabbix/api.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyzabbix/api.py b/pyzabbix/api.py index 04ecdee..8e959d9 100644 --- a/pyzabbix/api.py +++ b/pyzabbix/api.py @@ -32,6 +32,7 @@ # the urllib.request. import urllib.request as urllib2 +from . import __version__ from .logger import NullHandler, HideSensitiveFilter, HideSensitiveService null_handler = NullHandler() @@ -282,6 +283,7 @@ def do_request(self, method, params=None): req = urllib2.Request(self.url, data) req.get_method = lambda: 'POST' req.add_header('Content-Type', 'application/json-rpc') + req.add_header('User-Agent', 'py-zabbix/{}'.format(__version__)) if self.use_basic_auth: req.add_header("Authorization", "Basic {}".format(self.base64_cred)) From 2e98f9c1b22ff1b6431b6420687d38cb68ff6b28 Mon Sep 17 00:00:00 2001 From: Alexey Dubkov Date: Tue, 28 Apr 2020 23:25:04 -0700 Subject: [PATCH 14/15] move version to .version file for easier import with app versions --- pyzabbix/__init__.py | 2 -- pyzabbix/api.py | 2 +- pyzabbix/version.py | 1 + setup.py | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) create mode 100644 pyzabbix/version.py diff --git a/pyzabbix/__init__.py b/pyzabbix/__init__.py index 6a07f55..8a5fec2 100644 --- a/pyzabbix/__init__.py +++ b/pyzabbix/__init__.py @@ -1,4 +1,2 @@ from .api import ZabbixAPI, ZabbixAPIException, ssl_context_compat from .sender import ZabbixMetric, ZabbixSender, ZabbixResponse - -__version__ = '1.1.5' diff --git a/pyzabbix/api.py b/pyzabbix/api.py index 8e959d9..d1daf26 100644 --- a/pyzabbix/api.py +++ b/pyzabbix/api.py @@ -32,7 +32,7 @@ # the urllib.request. import urllib.request as urllib2 -from . import __version__ +from .version import __version__ from .logger import NullHandler, HideSensitiveFilter, HideSensitiveService null_handler = NullHandler() diff --git a/pyzabbix/version.py b/pyzabbix/version.py new file mode 100644 index 0000000..99d2a6f --- /dev/null +++ b/pyzabbix/version.py @@ -0,0 +1 @@ +__version__ = '1.1.5' diff --git a/setup.py b/setup.py index ac5703c..1820d3f 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ #!/usr/bin/env python from setuptools import setup -from pyzabbix import __version__ +from pyzabbix.version import __version__ setup(name='py-zabbix', version=__version__, From a7e7276be6400e5ee1ff381bd5f3df2c8595d3c3 Mon Sep 17 00:00:00 2001 From: Alexey Dubkov Date: Wed, 29 Apr 2020 00:31:22 -0700 Subject: [PATCH 15/15] bump version to 1.1.6 --- pyzabbix/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyzabbix/version.py b/pyzabbix/version.py index 99d2a6f..6ebd335 100644 --- a/pyzabbix/version.py +++ b/pyzabbix/version.py @@ -1 +1 @@ -__version__ = '1.1.5' +__version__ = '1.1.6'