├── .github
├── configs
│ ├── .htpasswd
│ ├── default.conf
│ ├── nginx.cnf
│ ├── pg_hba.conf
│ └── zabbix.conf.php
├── scripts
│ ├── additional_api_tests.py
│ ├── check_new_zabbx_release.py
│ ├── compatibility_api_test_5.py
│ ├── compatibility_api_test_6.py
│ ├── compatibility_api_test_7.py
│ ├── compatibility_api_test_latest.py
│ ├── depricated_tests.py
│ ├── integration_aioapi_test.py
│ ├── integration_aiogetter_test.py
│ ├── integration_aiosender_test.py
│ ├── integration_api_test.py
│ ├── integration_getter_test.py
│ ├── integration_sender_test.py
│ ├── library_import_tests.sh
│ ├── release_notification.py
│ ├── telegram_msg.py
│ └── wait_instance_zabbix.py
└── workflows
│ ├── additional_tests.yaml
│ ├── check_new_release.yaml
│ ├── compatibility_50.yaml
│ ├── compatibility_60.yaml
│ ├── compatibility_70.yaml
│ ├── compatibility_72.yaml
│ ├── compatibility_latest.yaml
│ ├── coverage.yaml
│ ├── depricated_tests.yaml
│ ├── integration_api.yaml
│ ├── integration_getter.yaml
│ ├── integration_sender.yaml
│ ├── release.yaml
│ └── tests.yaml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── examples
├── api
│ ├── asynchronous
│ │ ├── auth_by_token.py
│ │ ├── check_auth_state.py
│ │ ├── clear_history.py
│ │ ├── custom_client_session.py
│ │ ├── custom_ssl_context.py
│ │ ├── delete_items.py
│ │ ├── disabling_validate_certs.py
│ │ ├── export_templates.py
│ │ ├── get_request_parameters.py
│ │ ├── use_context_manager.py
│ │ └── using_http_auth.py
│ └── synchronous
│ │ ├── auth_by_token.py
│ │ ├── check_auth_state.py
│ │ ├── clear_history.py
│ │ ├── custom_ssl_context.py
│ │ ├── delete_items.py
│ │ ├── disabling_validate_certs.py
│ │ ├── export_templates.py
│ │ ├── get_request_parameters.py
│ │ ├── token_auth_if_supported.py
│ │ ├── use_context_manager.py
│ │ └── using_http_auth.py
├── get
│ ├── asynchronous
│ │ ├── custom_source_ip.py
│ │ └── getting_value.py
│ └── synchronous
│ │ ├── custom_source_ip.py
│ │ ├── getting_value.py
│ │ └── psk_wrapper.py
└── sender
│ ├── asynchronous
│ ├── agent_clusters_using.py
│ ├── agent_config_using.py
│ ├── bulk_sending.py
│ ├── custom_source_ip.py
│ ├── single_sending.py
│ ├── tls_cert_context.py
│ └── tls_cert_context_from_config.py
│ └── synchronous
│ ├── agent_clusters_using.py
│ ├── agent_config_using.py
│ ├── bulk_sending.py
│ ├── custom_source_ip.py
│ ├── psk_wrapper.py
│ ├── psk_wrapper_from_config.py
│ ├── single_sending.py
│ ├── tls_cert_wrapper.py
│ └── tls_cert_wrapper_from_config.py
├── requirements-dev.txt
├── requirements.txt
├── setup.cfg
├── setup.py
├── tests
├── __init__.py
├── common.py
├── test_zabbix_aioapi.py
├── test_zabbix_aiogetter.py
├── test_zabbix_aiosender.py
├── test_zabbix_api.py
├── test_zabbix_common.py
├── test_zabbix_getter.py
├── test_zabbix_sender.py
└── test_zabbix_types.py
└── zabbix_utils
├── __init__.py
├── aioapi.py
├── aiogetter.py
├── aiosender.py
├── api.py
├── common.py
├── exceptions.py
├── getter.py
├── logger.py
├── sender.py
├── types.py
└── version.py
/.github/configs/.htpasswd:
--------------------------------------------------------------------------------
1 | http_user:$apr1$aeHewW8J$rzWpr44lG7WSrsJRFflWS1
2 |
--------------------------------------------------------------------------------
/.github/configs/default.conf:
--------------------------------------------------------------------------------
1 | server {
2 | listen 80 default;
3 | listen 443 ssl;
4 | root /var/www/html/;
5 | index index.php;
6 |
7 | server_name localhost 127.0.0.1;
8 | ssl_certificate /etc/nginx/ssl/nginx.crt;
9 | ssl_certificate_key /etc/nginx/ssl/nginx.key;
10 |
11 | location /http_auth/ {
12 | auth_basic "Zabbix HTTP Auth";
13 | auth_basic_user_file /etc/nginx/.htpasswd;
14 |
15 | proxy_pass http://localhost:8080/;
16 | proxy_set_header Host $http_host;
17 | proxy_set_header X-Real-IP $remote_addr;
18 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
19 | proxy_buffering on;
20 | }
21 |
22 | location /ssl_context/ {
23 | proxy_pass http://localhost:8080/;
24 | proxy_set_header Host $http_host;
25 | proxy_set_header X-Real-IP $remote_addr;
26 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
27 | proxy_buffering on;
28 | }
29 | }
--------------------------------------------------------------------------------
/.github/configs/nginx.cnf:
--------------------------------------------------------------------------------
1 | [req]
2 | default_bits = 2048
3 | distinguished_name = req_distinguished_name
4 | req_extensions = req_ext
5 | x509_extensions = v3_req
6 | prompt = no
7 | [req_distinguished_name]
8 | countryName = LV
9 | localityName = Riga
10 | organizationName = Zabbix SIA
11 | organizationalUnitName = Integration team
12 | emailAddress = integrationteam@zabbix.com
13 | [req_ext]
14 | subjectAltName = @alt_names
15 | [v3_req]
16 | subjectAltName = @alt_names
17 | [alt_names]
18 | DNS.1 = localhost
19 | IP.1 = 127.0.0.1
--------------------------------------------------------------------------------
/.github/configs/pg_hba.conf:
--------------------------------------------------------------------------------
1 | local all postgres peer
2 | host zabbix zabbix 127.0.0.1/32 trust
3 | host zabbix zabbix localhost trust
4 | host zabbix zabbix 0.0.0.0/0 trust
5 | local all all peer
6 | host all all 127.0.0.1/32 scram-sha-256
7 | host all all ::1/128 scram-sha-256
8 | local replication all peer
9 | host replication all 127.0.0.1/32 scram-sha-256
10 | host replication all ::1/128 scram-sha-256
--------------------------------------------------------------------------------
/.github/configs/zabbix.conf.php:
--------------------------------------------------------------------------------
1 | zabbix_utils library supports <={sver} but the latest version of Zabbix is {max(versions)}.
44 | What to do next? Look at the manual: {MANUAL_REPO}.
45 | """
46 | sys.exit(error)
47 |
--------------------------------------------------------------------------------
/.github/scripts/depricated_tests.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # Copyright (C) 2001-2023 Zabbix SIA
3 | #
4 | # Zabbix SIA licenses this file under the MIT License.
5 | # See the LICENSE file in the project root for more information.
6 |
7 | import sys
8 | import base64
9 | import unittest
10 |
11 | sys.path.append('.')
12 | from zabbix_utils.api import ZabbixAPI
13 | from zabbix_utils.types import APIVersion
14 | from zabbix_utils.aioapi import AsyncZabbixAPI
15 |
16 | ZABBIX_URL = 'https://127.0.0.1:443'
17 | ZABBIX_USER = 'Admin'
18 | ZABBIX_PASSWORD = 'zabbix'
19 | HTTP_USER = 'http_user'
20 | HTTP_PASSWORD = 'http_pass'
21 |
22 |
23 | class BasicAuthAPITest(unittest.TestCase):
24 | """Test working with a real Zabbix API instance using Basic auth synchronously
25 |
26 | Should be removed after: `June 30, 2029`
27 | """
28 |
29 | def setUp(self):
30 | self.user = ZABBIX_USER
31 | self.password = ZABBIX_PASSWORD
32 | self.url = ZABBIX_URL + '/http_auth/'
33 | self.api = ZabbixAPI(
34 | url=self.url,
35 | user=self.user,
36 | password=self.password,
37 | validate_certs=False,
38 | http_user=HTTP_USER,
39 | http_password=HTTP_PASSWORD
40 | )
41 |
42 | def tearDown(self):
43 | if self.api:
44 | self.api.logout()
45 |
46 | def test_login(self):
47 | """Tests login function works properly"""
48 |
49 | self.assertEqual(
50 | type(self.api), ZabbixAPI, "Login was going wrong")
51 | self.assertEqual(
52 | type(self.api.api_version()), APIVersion, "Version getting was going wrong")
53 |
54 | def test_basic_auth(self):
55 | """Tests __basic_auth function works properly"""
56 |
57 | self.assertEqual(
58 | self.api._ZabbixAPI__basic_cred, base64.b64encode(
59 | "http_user:http_pass".encode()
60 | ).decode(), "Basic auth credentials generation was going wrong")
61 |
62 | def test_version_get(self):
63 | """Tests getting version info works properly"""
64 |
65 | version = None
66 | if self.api:
67 | version = self.api.apiinfo.version()
68 | self.assertEqual(
69 | version, str(self.api.api_version()), "Request apiinfo.version was going wrong")
70 |
71 | def test_check_auth(self):
72 | """Tests checking authentication state works properly"""
73 |
74 | resp = None
75 | if self.api:
76 | if self.api._ZabbixAPI__session_id == self.api._ZabbixAPI__token:
77 | resp = self.api.user.checkAuthentication(token=self.api._ZabbixAPI__session_id)
78 | else:
79 | resp = self.api.user.checkAuthentication(sessionid=self.api._ZabbixAPI__session_id)
80 | self.assertEqual(
81 | type(resp), dict, "Request user.checkAuthentication was going wrong")
82 |
83 | def test_user_get(self):
84 | """Tests getting users info works properly"""
85 |
86 | users = None
87 | if self.api:
88 | users = self.api.user.get(
89 | output=['userid', 'name']
90 | )
91 | self.assertEqual(type(users), list, "Request user.get was going wrong")
92 |
93 |
94 | class BasicAuthAsyncAPITest(unittest.IsolatedAsyncioTestCase):
95 | """Test working with a real Zabbix API instance using Basic auth asynchronously
96 |
97 | Should be removed after: `June 30, 2029`
98 | """
99 |
100 | async def asyncSetUp(self):
101 | self.user = ZABBIX_USER
102 | self.password = ZABBIX_PASSWORD
103 | self.url = ZABBIX_URL + '/http_auth/'
104 | self.api = AsyncZabbixAPI(
105 | url=self.url,
106 | validate_certs=False,
107 | http_user=HTTP_USER,
108 | http_password=HTTP_PASSWORD
109 | )
110 | await self.api.login(
111 | user=self.user,
112 | password=self.password
113 | )
114 |
115 | async def asyncTearDown(self):
116 | if self.api:
117 | await self.api.logout()
118 |
119 | async def test_login(self):
120 | """Tests login function works properly"""
121 |
122 | self.assertEqual(
123 | type(self.api), AsyncZabbixAPI, "Login was going wrong")
124 | self.assertEqual(
125 | type(self.api.api_version()), APIVersion, "Version getting was going wrong")
126 |
127 | async def test_basic_auth(self):
128 | """Tests __basic_auth function works properly"""
129 |
130 | basic_auth = self.api.client_session._default_auth
131 |
132 | self.assertEqual(
133 | base64.b64encode(f"{basic_auth.login}:{basic_auth.password}".encode()).decode(),
134 | base64.b64encode(f"{HTTP_USER}:{HTTP_PASSWORD}".encode()).decode(),
135 | "Basic auth credentials generation was going wrong"
136 | )
137 |
138 | async def test_version_get(self):
139 | """Tests getting version info works properly"""
140 |
141 | version = None
142 | if self.api:
143 | version = await self.api.apiinfo.version()
144 | self.assertEqual(
145 | version, str(self.api.api_version()), "Request apiinfo.version was going wrong")
146 |
147 | async def test_check_auth(self):
148 | """Tests checking authentication state works properly"""
149 |
150 | resp = None
151 | if self.api:
152 | if self.api._AsyncZabbixAPI__session_id == self.api._AsyncZabbixAPI__token:
153 | resp = await self.api.user.checkAuthentication(token=(self.api._AsyncZabbixAPI__session_id or ''))
154 | else:
155 | resp = await self.api.user.checkAuthentication(sessionid=(self.api._AsyncZabbixAPI__session_id or ''))
156 | self.assertEqual(
157 | type(resp), dict, "Request user.checkAuthentication was going wrong")
158 |
159 | async def test_user_get(self):
160 | """Tests getting users info works properly"""
161 |
162 | users = None
163 | if self.api:
164 | users = await self.api.user.get(
165 | output=['userid', 'name']
166 | )
167 | self.assertEqual(type(users), list, "Request user.get was going wrong")
168 |
169 |
170 | if __name__ == '__main__':
171 | unittest.main()
172 |
--------------------------------------------------------------------------------
/.github/scripts/integration_aioapi_test.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # Copyright (C) 2001-2023 Zabbix SIA
3 | #
4 | # Zabbix SIA licenses this file under the MIT License.
5 | # See the LICENSE file in the project root for more information.
6 |
7 | import sys
8 | import unittest
9 |
10 | sys.path.append('.')
11 | from zabbix_utils.aioapi import AsyncZabbixAPI
12 | from zabbix_utils.types import APIVersion
13 |
14 |
15 | class IntegrationAPITest(unittest.IsolatedAsyncioTestCase):
16 | """Test working with a real Zabbix API instance"""
17 |
18 | async def asyncSetUp(self):
19 | self.url = 'localhost'
20 | self.user = 'Admin'
21 | self.password = 'zabbix'
22 | self.zapi = AsyncZabbixAPI(
23 | url=self.url,
24 | skip_version_check=True
25 | )
26 | await self.zapi.login(
27 | user=self.user,
28 | password=self.password
29 | )
30 |
31 | async def asyncTearDown(self):
32 | if self.zapi:
33 | await self.zapi.logout()
34 |
35 | async def test_login(self):
36 | """Tests login function works properly"""
37 |
38 | self.assertEqual(
39 | type(self.zapi), AsyncZabbixAPI, "Login was going wrong")
40 | self.assertEqual(
41 | type(self.zapi.api_version()), APIVersion, "Version getting was going wrong")
42 |
43 | await self.zapi.logout()
44 |
45 | async def test_version_get(self):
46 | """Tests getting version info works properly"""
47 |
48 | version = None
49 | if self.zapi:
50 | version = await self.zapi.apiinfo.version()
51 | self.assertEqual(
52 | version, str(self.zapi.api_version()), "Request apiinfo.version was going wrong")
53 |
54 | async def test_check_auth(self):
55 | """Tests checking authentication state works properly"""
56 |
57 | resp = None
58 | if self.zapi:
59 | if self.zapi._AsyncZabbixAPI__session_id == self.zapi._AsyncZabbixAPI__token:
60 | resp = await self.zapi.user.checkAuthentication(token=self.zapi._AsyncZabbixAPI__session_id)
61 | else:
62 | resp = await self.zapi.user.checkAuthentication(sessionid=self.zapi._AsyncZabbixAPI__session_id)
63 | self.assertEqual(
64 | type(resp), dict, "Request user.checkAuthentication was going wrong")
65 |
66 | async def test_user_get(self):
67 | """Tests getting users info works properly"""
68 |
69 | users = None
70 | if self.zapi:
71 | users = await self.zapi.user.get(
72 | output=['userid', 'name']
73 | )
74 | self.assertEqual(type(users), list, "Request user.get was going wrong")
75 |
76 | async def test_host_get(self):
77 | """Tests getting hosts info works properly using suffix"""
78 |
79 | hosts = None
80 | if self.zapi:
81 | hosts = await self.zapi.host_.get_(
82 | output=['hostid', 'host']
83 | )
84 | self.assertEqual(type(hosts), list, "Request host.get was going wrong")
85 |
86 |
87 | if __name__ == '__main__':
88 | unittest.main()
89 |
--------------------------------------------------------------------------------
/.github/scripts/integration_aiogetter_test.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # Copyright (C) 2001-2023 Zabbix SIA
3 | #
4 | # Zabbix SIA licenses this file under the MIT License.
5 | # See the LICENSE file in the project root for more information.
6 |
7 | import sys
8 | import json
9 | import unittest
10 |
11 | sys.path.append('.')
12 | from zabbix_utils.aiogetter import AsyncGetter
13 |
14 |
15 | class IntegrationGetTest(unittest.IsolatedAsyncioTestCase):
16 | """Test working with a real Zabbix agent instance"""
17 |
18 | async def asyncSetUp(self):
19 | self.host = '127.0.0.1'
20 | self.port = 10050
21 | self.agent = AsyncGetter(
22 | host=self.host,
23 | port=self.port
24 | )
25 |
26 | async def test_get(self):
27 | """Tests getting item values from Zabbix agent works properly"""
28 |
29 | resp = await self.agent.get('net.if.discovery')
30 |
31 | self.assertIsNotNone(resp, "Getting item values was going wrong")
32 | try:
33 | resp_list = json.loads(resp.value)
34 | except json.decoder.JSONDecodeError:
35 | self.fail(f"raised unexpected Exception while parsing response: {resp}")
36 |
37 | self.assertEqual(type(resp_list), list, "Getting item values was going wrong")
38 | for resp in resp_list:
39 | self.assertEqual(type(resp), dict, "Getting item values was going wrong")
40 |
41 |
42 | if __name__ == '__main__':
43 | unittest.main()
44 |
--------------------------------------------------------------------------------
/.github/scripts/integration_aiosender_test.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # Copyright (C) 2001-2023 Zabbix SIA
3 | #
4 | # Zabbix SIA licenses this file under the MIT License.
5 | # See the LICENSE file in the project root for more information.
6 |
7 | import sys
8 | import unittest
9 |
10 | sys.path.append('.')
11 | from zabbix_utils.aiosender import AsyncSender
12 | from zabbix_utils.types import ItemValue, TrapperResponse, Node
13 |
14 |
15 | class IntegrationSenderTest(unittest.IsolatedAsyncioTestCase):
16 | """Test working with a real Zabbix server/proxy instance"""
17 |
18 | async def asyncSetUp(self):
19 | self.ip = '127.0.0.1'
20 | self.port = 10051
21 | self.chunk_size = 10
22 | self.sender = AsyncSender(
23 | server=self.ip,
24 | port=self.port,
25 | chunk_size=self.chunk_size
26 | )
27 |
28 | async def test_send(self):
29 | """Tests sending item values works properly"""
30 |
31 | items = [
32 | ItemValue('host1', 'item.key1', 10),
33 | ItemValue('host1', 'item.key2', 'test message'),
34 | ItemValue('host2', 'item.key1', -1, 1695713666),
35 | ItemValue('host3', 'item.key1', '{"msg":"test message"}'),
36 | ItemValue('host2', 'item.key1', 0, 1695713666, 100)
37 | ]
38 | response = await self.sender.send(items)
39 |
40 | self.assertEqual(type(response.details), dict, "Sending item values was going wrong")
41 | for node, resp in response.details.items():
42 | self.assertEqual(type(node), Node, "Sending item values was going wrong")
43 | for item in resp:
44 | self.assertEqual(type(item), TrapperResponse, "Sending item values was going wrong")
45 | for key in ('processed', 'failed', 'total', 'time', 'chunk'):
46 | try:
47 | self.assertIsNotNone(getattr(item, key), f"There aren't expected '{key}' value")
48 | except AttributeError:
49 | self.fail(f"raised unexpected Exception for attribute: {key}")
50 |
51 |
52 | if __name__ == '__main__':
53 | unittest.main()
54 |
--------------------------------------------------------------------------------
/.github/scripts/integration_api_test.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # Copyright (C) 2001-2023 Zabbix SIA
3 | #
4 | # Zabbix SIA licenses this file under the MIT License.
5 | # See the LICENSE file in the project root for more information.
6 |
7 | import sys
8 | import unittest
9 |
10 | sys.path.append('.')
11 | from zabbix_utils.api import ZabbixAPI
12 | from zabbix_utils.types import APIVersion
13 |
14 |
15 | class IntegrationAPITest(unittest.TestCase):
16 | """Test working with a real Zabbix API instance"""
17 |
18 | def setUp(self):
19 | self.url = 'localhost'
20 | self.user = 'Admin'
21 | self.password = 'zabbix'
22 | self.zapi = ZabbixAPI(
23 | url=self.url,
24 | user=self.user,
25 | password=self.password,
26 | skip_version_check=True
27 | )
28 |
29 | def tearDown(self):
30 | if self.zapi:
31 | self.zapi.logout()
32 |
33 | def test_login(self):
34 | """Tests login function works properly"""
35 |
36 | self.assertEqual(
37 | type(self.zapi), ZabbixAPI, "Login was going wrong")
38 | self.assertEqual(
39 | type(self.zapi.api_version()), APIVersion, "Version getting was going wrong")
40 |
41 | def test_version_get(self):
42 | """Tests getting version info works properly"""
43 |
44 | version = None
45 | if self.zapi:
46 | version = self.zapi.apiinfo.version()
47 | self.assertEqual(
48 | version, str(self.zapi.api_version()), "Request apiinfo.version was going wrong")
49 |
50 | def test_check_auth(self):
51 | """Tests checking authentication state works properly"""
52 |
53 | resp = None
54 | if self.zapi:
55 | if self.zapi._ZabbixAPI__session_id == self.zapi._ZabbixAPI__token:
56 | resp = self.zapi.user.checkAuthentication(token=self.zapi._ZabbixAPI__session_id)
57 | else:
58 | resp = self.zapi.user.checkAuthentication(sessionid=self.zapi._ZabbixAPI__session_id)
59 | self.assertEqual(
60 | type(resp), dict, "Request user.checkAuthentication was going wrong")
61 |
62 | def test_user_get(self):
63 | """Tests getting users info works properly"""
64 |
65 | users = None
66 | if self.zapi:
67 | users = self.zapi.user.get(
68 | output=['userid', 'name']
69 | )
70 | self.assertEqual(type(users), list, "Request user.get was going wrong")
71 |
72 | def test_host_get(self):
73 | """Tests getting hosts info works properly using suffix"""
74 |
75 | hosts = None
76 | if self.zapi:
77 | hosts = self.zapi.host_.get_(
78 | output=['hostid', 'host']
79 | )
80 | self.assertEqual(type(hosts), list, "Request host.get was going wrong")
81 |
82 |
83 | if __name__ == '__main__':
84 | unittest.main()
85 |
--------------------------------------------------------------------------------
/.github/scripts/integration_getter_test.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # Copyright (C) 2001-2023 Zabbix SIA
3 | #
4 | # Zabbix SIA licenses this file under the MIT License.
5 | # See the LICENSE file in the project root for more information.
6 |
7 | import sys
8 | import json
9 | import unittest
10 |
11 | sys.path.append('.')
12 | from zabbix_utils.getter import Getter
13 |
14 |
15 | class IntegrationGetTest(unittest.TestCase):
16 | """Test working with a real Zabbix agent instance"""
17 |
18 | def setUp(self):
19 | self.host = '127.0.0.1'
20 | self.port = 10050
21 | self.agent = Getter(
22 | host=self.host,
23 | port=self.port
24 | )
25 |
26 | def test_get(self):
27 | """Tests getting item values from Zabbix agent works properly"""
28 |
29 | resp = self.agent.get('net.if.discovery')
30 |
31 | self.assertIsNotNone(resp, "Getting item values was going wrong")
32 | try:
33 | resp_list = json.loads(resp.value)
34 | except json.decoder.JSONDecodeError:
35 | self.fail(f"raised unexpected Exception while parsing response: {resp}")
36 |
37 | self.assertEqual(type(resp_list), list, "Getting item values was going wrong")
38 | for resp in resp_list:
39 | self.assertEqual(type(resp), dict, "Getting item values was going wrong")
40 |
41 |
42 | if __name__ == '__main__':
43 | unittest.main()
44 |
--------------------------------------------------------------------------------
/.github/scripts/integration_sender_test.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # Copyright (C) 2001-2023 Zabbix SIA
3 | #
4 | # Zabbix SIA licenses this file under the MIT License.
5 | # See the LICENSE file in the project root for more information.
6 |
7 | import sys
8 | import unittest
9 |
10 | sys.path.append('.')
11 | from zabbix_utils.sender import Sender
12 | from zabbix_utils.types import ItemValue, TrapperResponse, Node
13 |
14 |
15 | class IntegrationSenderTest(unittest.TestCase):
16 | """Test working with a real Zabbix server/proxy instance"""
17 |
18 | def setUp(self):
19 | self.ip = '127.0.0.1'
20 | self.port = 10051
21 | self.chunk_size = 10
22 | self.sender = Sender(
23 | server=self.ip,
24 | port=self.port,
25 | chunk_size=self.chunk_size
26 | )
27 |
28 | def test_send(self):
29 | """Tests sending item values works properly"""
30 |
31 | items = [
32 | ItemValue('host1', 'item.key1', 10),
33 | ItemValue('host1', 'item.key2', 'test message'),
34 | ItemValue('host2', 'item.key1', -1, 1695713666),
35 | ItemValue('host3', 'item.key1', '{"msg":"test message"}'),
36 | ItemValue('host2', 'item.key1', 0, 1695713666, 100)
37 | ]
38 | response = self.sender.send(items)
39 |
40 | self.assertEqual(type(response.details), dict, "Sending item values was going wrong")
41 | for node, resp in response.details.items():
42 | self.assertEqual(type(node), Node, "Sending item values was going wrong")
43 | for item in resp:
44 | self.assertEqual(type(item), TrapperResponse, "Sending item values was going wrong")
45 | for key in ('processed', 'failed', 'total', 'time', 'chunk'):
46 | try:
47 | self.assertIsNotNone(getattr(item, key), f"There aren't expected '{key}' value")
48 | except AttributeError:
49 | self.fail(f"raised unexpected Exception for attribute: {key}")
50 |
51 |
52 | if __name__ == '__main__':
53 | unittest.main()
54 |
--------------------------------------------------------------------------------
/.github/scripts/library_import_tests.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | mode=$1
4 | class=$2
5 | error=$3
6 |
7 | cmd="import sys; sys.path.append('.'); from zabbix_utils import $class; $class()"
8 | if [ $mode == "async" ]; then
9 | cmd="import sys; import asyncio; sys.path.append('.'); from zabbix_utils import $class; exec('async def main():\n $class()'); asyncio.run(main())"
10 | fi
11 |
12 | result=$(python3 -c "$cmd" 2>&1)
13 | echo "$result" | grep "$error" >/dev/null || echo "$result" | (python3 "./.github/scripts/telegram_msg.py" && echo "Error")
14 |
--------------------------------------------------------------------------------
/.github/scripts/release_notification.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # coding: utf-8
3 |
4 | import os
5 | import json
6 | import smtplib
7 | import markdown
8 |
9 | from email.mime.text import MIMEText
10 | from email.mime.multipart import MIMEMultipart
11 |
12 | # Repository constants
13 | LIBRARY_VERSION = os.environ['LIBRARY_VERSION']
14 | REPOSITORY = os.environ['REPOSITORY']
15 |
16 | # Mail server variables
17 | mail_port = int(os.environ.get('MAIL_PORT', '465'))
18 | mail_server = os.environ['MAIL_SERVER']
19 | auth_user = os.environ['MAIL_USER']
20 | auth_pass = os.environ['MAIL_PASS']
21 |
22 | # Mail variables
23 | mail_from = '"zabbix_utils" <' + auth_user + '>'
24 | mail_to = json.loads(os.environ['RELEASE_RECIPIENT_LIST'])
25 | mail_subject = f"[GitHub] A new version {LIBRARY_VERSION} of the zabbix_utils library has been released"
26 | mail_text = f"""
27 | A new version of the zabbix_utils library has been released:
28 | v{LIBRARY_VERSION}
29 |
30 |
31 | """
32 |
33 | # Reading release notes
34 | with open("RELEASE_NOTES.md", "r", encoding="utf-8") as fh:
35 | release_notes = markdown.markdown("\n".join(fh.readlines()[1:]))
36 |
37 | # Preparing mail data
38 | msg = MIMEMultipart('mixed')
39 | msg['Subject'] = mail_subject
40 | msg['From'] = mail_from
41 | msg['To'] = ', '.join(mail_to)
42 |
43 | # Adding message text
44 | msg.attach(MIMEText(mail_text + release_notes, 'html'))
45 |
46 | # Connection to the mail server
47 | server = smtplib.SMTP_SSL(mail_server, mail_port)
48 | server.login(auth_user, auth_pass)
49 |
50 | # Sending email
51 | server.sendmail(mail_from, mail_to, msg.as_string())
52 |
53 | # Closing connection
54 | server.quit()
55 |
--------------------------------------------------------------------------------
/.github/scripts/telegram_msg.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # Copyright (C) 2001-2023 Zabbix SIA
3 | #
4 | # Zabbix SIA licenses this file under the MIT License.
5 | # See the LICENSE file in the project root for more information.
6 |
7 | import os
8 | import sys
9 | import json
10 | import requests
11 |
12 | chat_id = os.environ.get("TBOT_CHAT") # chat id. env TBOT_CHAT must be set!
13 | token = os.environ.get("TBOT_TOKEN") # bot token. env TBOT_TOKEN must be set!
14 | parse_mode = os.environ.get("TBOT_FORMAT", '') # HTML, MarkdownV2 or empty
15 |
16 | for key in ["TBOT_CHAT", "TBOT_TOKEN"]:
17 | if not os.environ.get(key):
18 | print(f"Please set environmental variable \"{key}\"")
19 | sys.exit(1)
20 |
21 |
22 | def sendMessage(msg, passthrough=True):
23 |
24 | if passthrough:
25 | print(msg)
26 |
27 | if len(msg) == 0:
28 | return '{"ok":true}'
29 |
30 | url = f"https://api.telegram.org/bot{token}/sendMessage"
31 |
32 | if len(msg) > 4096:
33 | msg = "Message output is too long. Please check the GitHub action log."
34 |
35 | if os.environ.get("SUBJECT"):
36 | msg = f'{os.environ.get("SUBJECT")}\n\n{msg}'
37 |
38 | if os.environ.get("GH_JOB"):
39 | msg += f'\n\n{os.environ.get("GH_JOB")}'
40 |
41 | payload = {
42 | "text": msg,
43 | "parse_mode": parse_mode,
44 | "disable_web_page_preview": False,
45 | "disable_notification": False,
46 | "reply_to_message_id": None,
47 | "chat_id": chat_id
48 | }
49 | headers = {
50 | "accept": "application/json",
51 | "User-Agent": "Python script",
52 | "content-type": "application/json"
53 | }
54 |
55 | response = requests.post(url, json=payload, headers=headers)
56 |
57 | return response.text
58 |
59 |
60 | if len(sys.argv) == 2:
61 | message = sys.argv[1]
62 | else:
63 | message = sys.stdin.read()
64 |
65 | if not message:
66 | sys.exit(0)
67 |
68 | result = json.loads(sendMessage(message))
69 | if not result["ok"]:
70 | print(result["error_code"], result["description"])
71 | sys.exit(1)
72 |
--------------------------------------------------------------------------------
/.github/scripts/wait_instance_zabbix.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # Copyright (C) 2001-2023 Zabbix SIA
3 | #
4 | # Zabbix SIA licenses this file under the MIT License.
5 | # See the LICENSE file in the project root for more information.
6 |
7 | import sys
8 | import time
9 |
10 | sys.path.append('.')
11 | from zabbix_utils import ZabbixAPI, APIRequestError
12 |
13 | for x in range(20):
14 | try:
15 | zapi = ZabbixAPI(
16 | url="localhost",
17 | user="Admin",
18 | password="zabbix",
19 | skip_version_check=True
20 | )
21 | except APIRequestError as error:
22 | print(f'Zabbix API is not ready... Data: {error}', flush=True)
23 | time.sleep(5)
24 | else:
25 | zapi.logout()
26 | sys.exit(0)
27 | sys.exit('Failed to wait for Zabbix API to be ready')
28 |
--------------------------------------------------------------------------------
/.github/workflows/additional_tests.yaml:
--------------------------------------------------------------------------------
1 | name: additional_tests
2 | run-name: Additional tests for API features
3 |
4 | on:
5 | push:
6 | branches: [main]
7 | pull_request:
8 | branches: [main]
9 | workflow_dispatch:
10 |
11 | env:
12 | ZABBIX_BRANCH: master
13 | CONFIG_PATH: .github/configs/
14 | TEST_FILE: additional_api_tests.py
15 |
16 | jobs:
17 | importing-tests:
18 | name: Importing tests
19 | runs-on: ubuntu-22.04
20 |
21 | steps:
22 | - uses: actions/checkout@v3
23 | - name: Install Python
24 | run: |
25 | sudo apt update && sudo apt-get install -y python3 python3-pip python-is-python3
26 | - name: Prepare environment
27 | run: |
28 | touch /tmp/importing.log
29 | - name: Check import of sync without requirements
30 | continue-on-error: true
31 | env:
32 | TBOT_TOKEN: ${{ secrets.TBOT_TOKEN }}
33 | TBOT_CHAT: ${{ vars.TBOT_CHAT }}
34 | SUBJECT: Importing test without requirements FAIL
35 | run: |
36 | bash ./.github/scripts/library_import_tests.sh "sync" "ZabbixAPI" "Unable to connect to" > /tmp/importing.log
37 | - name: Check import of async without requirements
38 | continue-on-error: true
39 | env:
40 | TBOT_TOKEN: ${{ secrets.TBOT_TOKEN }}
41 | TBOT_CHAT: ${{ vars.TBOT_CHAT }}
42 | SUBJECT: Importing test without requirements FAIL
43 | run: |
44 | bash ./.github/scripts/library_import_tests.sh "async" "AsyncZabbixAPI" "ModuleNotFoundError:" > /tmp/importing.log
45 | - name: Install requirements
46 | run: |
47 | pip install -r ./requirements.txt
48 | - name: Check import of async with requirements
49 | continue-on-error: true
50 | env:
51 | TBOT_TOKEN: ${{ secrets.TBOT_TOKEN }}
52 | TBOT_CHAT: ${{ vars.TBOT_CHAT }}
53 | SUBJECT: Importing tests with requirements FAIL
54 | run: |
55 | bash ./.github/scripts/library_import_tests.sh "async" "AsyncZabbixAPI" "Unable to connect to" > /tmp/importing.log
56 | - name: Raise an exception
57 | run: |
58 | test $(cat /tmp/importing.log | wc -l) -eq 0 || exit 1
59 | additional-tests:
60 | name: Additional tests
61 | runs-on: ubuntu-latest
62 |
63 | steps:
64 | - uses: actions/checkout@v4
65 | - name: Install packages
66 | run: |
67 | sudo apt update && sudo apt install -y git sudo nginx gcc make automake pkg-config postgresql-14 libpostgresql-ocaml-dev libxml2-dev libpcre3-dev libevent-dev apache2 libapache2-mod-php php8.1-pgsql php8.1-bcmath php8.1-xml php8.1-gd php8.1-ldap php8.1-mbstring libzip-dev
68 | - name: Build from sources
69 | run: |
70 | WORKDIR=$(pwd)
71 | cd /tmp/
72 | git -c advice.detachedHead=false clone https://git.zabbix.com/scm/zbx/zabbix.git --branch ${{ env.ZABBIX_BRANCH }} --depth 1 --single-branch /tmp/zabbix-branch
73 | cd /tmp/zabbix-branch
74 | ./bootstrap.sh
75 | ./configure --enable-server --with-postgresql
76 | sudo make dbschema_postgresql
77 | echo -e "CacheUpdateFrequency=1\n" >> ./conf/zabbix_server.conf
78 | sudo mkdir -p /etc/nginx/ssl/
79 | sudo cp $WORKDIR/${{ env.CONFIG_PATH }}/.htpasswd /etc/nginx/.htpasswd
80 | sudo cp $WORKDIR/${{ env.CONFIG_PATH }}/default.conf /etc/nginx/sites-enabled/default
81 | sudo openssl req -x509 -nodes -days 1 -newkey rsa:2048 -keyout /etc/nginx/ssl/nginx.key -out /etc/nginx/ssl/nginx.crt -config $WORKDIR/${{ env.CONFIG_PATH }}/nginx.cnf
82 | sudo chown -R www-data:www-data /etc/nginx/
83 | cd ui
84 | sudo rm /var/www/html/index.html
85 | sudo cp -a . /var/www/html/
86 | sudo cp $WORKDIR/${{ env.CONFIG_PATH }}/zabbix.conf.php /var/www/html/conf/
87 | sudo cp $WORKDIR/${{ env.CONFIG_PATH }}/pg_hba.conf /etc/postgresql/14/main/pg_hba.conf
88 | sudo chown -R www-data:www-data /var/www/html/
89 | sudo sed -i "s/post_max_size = 8M/post_max_size = 16M/g" /etc/php/8.1/apache2/php.ini
90 | sudo sed -i "s/max_execution_time = 30/max_execution_time = 300/g" /etc/php/8.1/apache2/php.ini
91 | sudo sed -i "s/max_input_time = 60/max_input_time = 300/g" /etc/php/8.1/apache2/php.ini
92 | sudo sed -i "s/Listen 80/Listen 8080/g" /etc/apache2/ports.conf
93 | sudo sed -i "s///g" /etc/apache2/sites-enabled/000-default.conf
94 | sudo locale-gen en_US.UTF-8
95 | sudo update-locale
96 | - name: Prepare environment
97 | run: |
98 | sudo addgroup --system --quiet zabbix
99 | sudo adduser --quiet --system --disabled-login --ingroup zabbix --home /var/lib/zabbix --no-create-home zabbix
100 | sudo mkdir -p /var/run/postgresql/14-main.pg_stat_tmp
101 | sudo touch /var/run/postgresql/14-main.pg_stat_tmp/global.tmp
102 | sudo chmod 0777 /var/run/postgresql/14-main.pg_stat_tmp/global.tmp
103 | (sudo -u postgres /usr/lib/postgresql/14/bin/postgres -D /var/lib/postgresql/14/main -c config_file=/etc/postgresql/14/main/postgresql.conf)&
104 | sleep 5
105 | cd /tmp/zabbix-branch/database/postgresql
106 | sudo -u postgres createuser zabbix
107 | sudo -u postgres createdb -O zabbix -E Unicode -T template0 zabbix
108 | cat schema.sql | sudo -u zabbix psql zabbix
109 | cat images.sql | sudo -u zabbix psql zabbix
110 | cat data.sql | sudo -u zabbix psql zabbix
111 | - name: Start Apache & Nginx
112 | run: |
113 | sudo apache2ctl start
114 | sudo nginx -g "daemon on; master_process on;"
115 | - name: Install python3
116 | run: |
117 | sudo apt-get install -y python3 python3-pip python-is-python3
118 | pip install -r ./requirements.txt
119 | - name: Run tests
120 | continue-on-error: true
121 | run: |
122 | sleep 5
123 | python ./.github/scripts/$TEST_FILE 2>/tmp/additional.log >/dev/null
124 | - name: Send report
125 | env:
126 | TBOT_TOKEN: ${{ secrets.TBOT_TOKEN }}
127 | TBOT_CHAT: ${{ vars.TBOT_CHAT }}
128 | SUBJECT: Zabbix API additional tests FAIL
129 | run: |
130 | tail -n1 /tmp/additional.log | grep "OK" 1>/dev/null || tail /tmp/additional.log | python ./.github/scripts/telegram_msg.py | exit 1
131 |
--------------------------------------------------------------------------------
/.github/workflows/check_new_release.yaml:
--------------------------------------------------------------------------------
1 | name: check_new_release
2 | run-name: Check new Zabbix release
3 |
4 | on:
5 | schedule:
6 | - cron: "0 0 * * *"
7 | workflow_dispatch:
8 |
9 | jobs:
10 | check-release:
11 | name: Check Zabbix release
12 | runs-on: ubuntu-latest
13 |
14 | steps:
15 | - uses: actions/checkout@v4
16 | - name: Prepare environment
17 | run: |
18 | sudo apt-get install -y python3 python3-pip python-is-python3
19 | pip install -r ./requirements.txt
20 | - name: Check new Zabbix release
21 | env:
22 | BRANCHES_URL: ${{ vars.BRANCHES_URL }}
23 | LIBREPO_URL: ${{ vars.LIBREPO_URL }}
24 | MANUAL_REPO: ${{ vars.MANUAL_REPO }}
25 | run: |
26 | python ./.github/scripts/check_new_zabbx_release.py 2>/tmp/check_release.log || echo
27 | - name: Send report
28 | env:
29 | TBOT_TOKEN: ${{ secrets.TBOT_TOKEN }}
30 | TBOT_CHAT: ${{ vars.TBOT_CHAT }}
31 | TBOT_FORMAT: html
32 | SUBJECT: zabbix_utils repo requires update due new Zabbix release
33 | run: |
34 | tail /tmp/check_release.log | python ./.github/scripts/telegram_msg.py
35 |
--------------------------------------------------------------------------------
/.github/workflows/compatibility_50.yaml:
--------------------------------------------------------------------------------
1 | name: zabbix_50
2 | run-name: Compatibility with Zabbix 5.0 test
3 |
4 | on:
5 | push:
6 | branches: [main]
7 | pull_request:
8 | branches: [main]
9 | workflow_dispatch:
10 |
11 | env:
12 | ZABBIX_VERSION: '5.0'
13 | ZABBIX_BRANCH: release/$ZABBIX_VERSION
14 | CONFIG_PATH: .github/configs/
15 | TEST_FILE: compatibility_api_test_5.py
16 |
17 | jobs:
18 | compatibility:
19 | name: Compatibility test
20 | runs-on: ubuntu-20.04
21 |
22 | steps:
23 | - uses: actions/checkout@v3
24 | - name: Install packages
25 | run: |
26 | curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/postgresql.gpg
27 | echo "deb http://apt.postgresql.org/pub/repos/apt/ `lsb_release -cs`-pgdg main" | sudo tee /etc/apt/sources.list.d/pgdg.list
28 | sudo add-apt-repository ppa:ondrej/php
29 | sudo apt update && sudo apt install -y git sudo gcc make automake pkg-config postgresql-14 libpostgresql-ocaml-dev libxml2-dev libpcre3-dev libevent-dev apache2 libzip-dev php7.4 libapache2-mod-php7.4 php7.4-common php7.4-fpm php7.4-gd php7.4-mbstring php7.4-xml php7.4-zip php7.4-ldap
30 | - name: Build from sources
31 | run: |
32 | WORKDIR=$(pwd)
33 | cd /tmp/
34 | git -c advice.detachedHead=false clone https://git.zabbix.com/scm/zbx/zabbix.git --branch ${{ env.ZABBIX_BRANCH }} --depth 1 --single-branch /tmp/zabbix-branch
35 | cd /tmp/zabbix-branch
36 | ./bootstrap.sh
37 | ./configure --enable-server --enable-agent --with-postgresql
38 | sudo make dbschema_postgresql
39 | sudo make
40 | echo -e "CacheUpdateFrequency=1\n" >> ./conf/zabbix_server.conf
41 | cd ui
42 | sudo rm /var/www/html/index.html
43 | sudo cp -a . /var/www/html/
44 | sudo cp $WORKDIR/${{ env.CONFIG_PATH }}/zabbix.conf.php /var/www/html/conf/
45 | sudo cp $WORKDIR/${{ env.CONFIG_PATH }}/pg_hba.conf /etc/postgresql/14/main/pg_hba.conf
46 | sudo chown -R www-data:www-data /var/www/html/
47 | sudo sed -i "s/post_max_size = 8M/post_max_size = 16M/g" /etc/php/7.4/fpm/php.ini
48 | sudo sed -i "s/max_execution_time = 30/max_execution_time = 300/g" /etc/php/7.4/fpm/php.ini
49 | sudo sed -i "s/max_input_time = 60/max_input_time = 300/g" /etc/php/7.4/fpm/php.ini
50 | sudo locale-gen en_US.UTF-8
51 | sudo update-locale
52 | - name: Prepare environment
53 | run: |
54 | sudo addgroup --system --quiet zabbix
55 | sudo adduser --quiet --system --disabled-login --ingroup zabbix --home /var/lib/zabbix --no-create-home zabbix
56 | sudo mkdir -p /var/run/postgresql/14-main.pg_stat_tmp
57 | sudo chown -R postgres. /var/run/postgresql/
58 | sudo touch /var/run/postgresql/14-main.pg_stat_tmp/global.tmp
59 | sudo chmod 0777 /var/run/postgresql/14-main.pg_stat_tmp/global.tmp
60 | sudo sed -i 's/port = 5433/port = 5432/' /etc/postgresql/14/main/postgresql.conf
61 | (sudo -u postgres /usr/lib/postgresql/14/bin/postgres -D /var/lib/postgresql/14/main -c config_file=/etc/postgresql/14/main/postgresql.conf)&
62 | sleep 5
63 | cd /tmp/zabbix-branch/database/postgresql
64 | sudo -u postgres createuser zabbix
65 | sudo -u postgres createdb -O zabbix -E Unicode -T template0 zabbix
66 | cat schema.sql | sudo -u zabbix psql zabbix
67 | cat images.sql | sudo -u zabbix psql zabbix
68 | cat data.sql | sudo -u zabbix psql zabbix
69 | sudo apache2ctl start
70 | - name: Start Zabbix server
71 | run: |
72 | cd /tmp/zabbix-branch
73 | sudo ./src/zabbix_server/zabbix_server -c ./conf/zabbix_server.conf
74 | - name: Start Zabbix agent
75 | run: |
76 | cd /tmp/zabbix-branch
77 | sudo ./src/zabbix_agent/zabbix_agentd -c ./conf/zabbix_agentd.conf
78 | - name: Install python3
79 | run: |
80 | sudo apt-get install -y python3 python3-pip python-is-python3
81 | pip install -r ./requirements.txt
82 | - name: Wait for Zabbix API
83 | run: |
84 | python ./.github/scripts/wait_instance_zabbix.py
85 | - name: Compatibility test
86 | continue-on-error: true
87 | run: |
88 | python ./.github/scripts/$TEST_FILE 2>/tmp/compatibility.log >/dev/null
89 | - name: Send report
90 | env:
91 | TBOT_TOKEN: ${{ secrets.TBOT_TOKEN }}
92 | TBOT_CHAT: ${{ vars.TBOT_CHAT }}
93 | SUBJECT: Compatibility with Zabbix ${{ env.ZABBIX_VERSION }} FAIL
94 | run: |
95 | tail -n1 /tmp/compatibility.log | grep "OK" 1>/dev/null || tail /tmp/compatibility.log | python ./.github/scripts/telegram_msg.py | exit 1
96 |
--------------------------------------------------------------------------------
/.github/workflows/compatibility_60.yaml:
--------------------------------------------------------------------------------
1 | name: zabbix_60
2 | run-name: Compatibility with Zabbix 6.0 test
3 |
4 | on:
5 | push:
6 | branches: [main]
7 | pull_request:
8 | branches: [main]
9 | workflow_dispatch:
10 |
11 | env:
12 | ZABBIX_VERSION: '6.0'
13 | ZABBIX_BRANCH: release/$ZABBIX_VERSION
14 | CONFIG_PATH: .github/configs/
15 | TEST_FILE: compatibility_api_test_6.py
16 |
17 | jobs:
18 | compatibility:
19 | name: Compatibility test
20 | runs-on: ubuntu-22.04
21 |
22 | steps:
23 | - uses: actions/checkout@v3
24 | - name: Install packages
25 | run: |
26 | sudo apt update && sudo apt install -y git sudo gcc make automake pkg-config postgresql-14 libpostgresql-ocaml-dev libxml2-dev libpcre3-dev libevent-dev apache2 libapache2-mod-php php8.1-pgsql php8.1-bcmath php8.1-xml php8.1-gd php8.1-ldap php8.1-mbstring libzip-dev
27 | - name: Build from sources
28 | run: |
29 | WORKDIR=$(pwd)
30 | cd /tmp/
31 | git -c advice.detachedHead=false clone https://git.zabbix.com/scm/zbx/zabbix.git --branch ${{ env.ZABBIX_BRANCH }} --depth 1 --single-branch /tmp/zabbix-branch
32 | cd /tmp/zabbix-branch
33 | ./bootstrap.sh
34 | ./configure --enable-server --enable-agent --with-postgresql
35 | sudo make dbschema_postgresql
36 | sudo make
37 | echo -e "CacheUpdateFrequency=1\n" >> ./conf/zabbix_server.conf
38 | cd ui
39 | sudo rm /var/www/html/index.html
40 | sudo cp -a . /var/www/html/
41 | sudo cp $WORKDIR/${{ env.CONFIG_PATH }}/zabbix.conf.php /var/www/html/conf/
42 | sudo cp $WORKDIR/${{ env.CONFIG_PATH }}/pg_hba.conf /etc/postgresql/14/main/pg_hba.conf
43 | sudo chown -R www-data:www-data /var/www/html/
44 | sudo sed -i "s/post_max_size = 8M/post_max_size = 16M/g" /etc/php/8.1/apache2/php.ini
45 | sudo sed -i "s/max_execution_time = 30/max_execution_time = 300/g" /etc/php/8.1/apache2/php.ini
46 | sudo sed -i "s/max_input_time = 60/max_input_time = 300/g" /etc/php/8.1/apache2/php.ini
47 | sudo locale-gen en_US.UTF-8
48 | sudo update-locale
49 | - name: Prepare environment
50 | run: |
51 | sudo addgroup --system --quiet zabbix
52 | sudo adduser --quiet --system --disabled-login --ingroup zabbix --home /var/lib/zabbix --no-create-home zabbix
53 | sudo mkdir -p /var/run/postgresql/14-main.pg_stat_tmp
54 | sudo touch /var/run/postgresql/14-main.pg_stat_tmp/global.tmp
55 | sudo chmod 0777 /var/run/postgresql/14-main.pg_stat_tmp/global.tmp
56 | (sudo -u postgres /usr/lib/postgresql/14/bin/postgres -D /var/lib/postgresql/14/main -c config_file=/etc/postgresql/14/main/postgresql.conf)&
57 | sleep 5
58 | cd /tmp/zabbix-branch/database/postgresql
59 | sudo -u postgres createuser zabbix
60 | sudo -u postgres createdb -O zabbix -E Unicode -T template0 zabbix
61 | cat schema.sql | sudo -u zabbix psql zabbix
62 | cat images.sql | sudo -u zabbix psql zabbix
63 | cat data.sql | sudo -u zabbix psql zabbix
64 | sudo apache2ctl start
65 | - name: Start Zabbix server
66 | run: |
67 | cd /tmp/zabbix-branch
68 | sudo ./src/zabbix_server/zabbix_server -c ./conf/zabbix_server.conf
69 | - name: Start Zabbix agent
70 | run: |
71 | cd /tmp/zabbix-branch
72 | sudo ./src/zabbix_agent/zabbix_agentd -c ./conf/zabbix_agentd.conf
73 | - name: Install python3
74 | run: |
75 | sudo apt-get install -y python3 python3-pip python-is-python3
76 | pip install -r ./requirements.txt
77 | - name: Wait for Zabbix API
78 | run: |
79 | python ./.github/scripts/wait_instance_zabbix.py
80 | - name: Compatibility test
81 | continue-on-error: true
82 | run: |
83 | python ./.github/scripts/$TEST_FILE 2>/tmp/compatibility.log >/dev/null
84 | - name: Send report
85 | env:
86 | TBOT_TOKEN: ${{ secrets.TBOT_TOKEN }}
87 | TBOT_CHAT: ${{ vars.TBOT_CHAT }}
88 | SUBJECT: Compatibility with Zabbix ${{ env.ZABBIX_VERSION }} FAIL
89 | run: |
90 | tail -n1 /tmp/compatibility.log | grep "OK" 1>/dev/null || tail /tmp/compatibility.log | python ./.github/scripts/telegram_msg.py | exit 1
91 |
--------------------------------------------------------------------------------
/.github/workflows/compatibility_70.yaml:
--------------------------------------------------------------------------------
1 | name: zabbix_70
2 | run-name: Compatibility with Zabbix 7.0 test
3 |
4 | on:
5 | push:
6 | branches: [main]
7 | pull_request:
8 | branches: [main]
9 | workflow_dispatch:
10 |
11 | env:
12 | ZABBIX_VERSION: '7.0'
13 | ZABBIX_BRANCH: release/$ZABBIX_VERSION
14 | CONFIG_PATH: .github/configs/
15 | TEST_FILE: compatibility_api_test_7.py
16 |
17 | jobs:
18 | compatibility:
19 | name: Compatibility test
20 | runs-on: ubuntu-24.04
21 |
22 | steps:
23 | - uses: actions/checkout@v4
24 | - name: Install packages
25 | run: |
26 | sudo apt update && sudo apt install -y git sudo gcc make automake pkg-config postgresql-16 libpostgresql-ocaml-dev libxml2-dev libpcre3-dev libevent-dev apache2 libapache2-mod-php php8.3-pgsql php8.3-bcmath php8.3-xml php8.3-gd php8.3-ldap php8.3-mbstring libzip-dev
27 | - name: Build from sources
28 | run: |
29 | WORKDIR=$(pwd)
30 | cd /tmp/
31 | sudo addgroup --system --quiet zabbix
32 | sudo adduser --quiet --system --disabled-login --ingroup zabbix --home /var/lib/zabbix --no-create-home zabbix
33 | git -c advice.detachedHead=false clone https://git.zabbix.com/scm/zbx/zabbix.git --branch ${{ env.ZABBIX_BRANCH }} --depth 1 --single-branch /tmp/zabbix-branch
34 | cd /tmp/zabbix-branch
35 | ./bootstrap.sh
36 | ./configure --enable-server --enable-agent --with-postgresql
37 | sudo make dbschema_postgresql
38 | sudo make
39 | echo -e "CacheUpdateFrequency=1\n" >> ./conf/zabbix_server.conf
40 | ./configure --enable-proxy --with-sqlite3
41 | sudo make
42 | mkdir /tmp/zabbix_proxy1/
43 | mkdir /tmp/zabbix_proxy2/
44 | cp ./conf/zabbix_proxy.conf ./conf/zabbix_proxy1.conf
45 | mv ./conf/zabbix_proxy.conf ./conf/zabbix_proxy2.conf
46 | sed -i "s/Hostname=Zabbix proxy/Hostname=CompatibilitySenderTest_proxy1/g" ./conf/zabbix_proxy1.conf
47 | sed -i "s/Hostname=Zabbix proxy/Hostname=CompatibilitySenderTest_proxy2/g" ./conf/zabbix_proxy2.conf
48 | sed -i "s#LogFile=/tmp/zabbix_proxy.log#LogFile=/tmp/zabbix_proxy1.log#g" ./conf/zabbix_proxy1.conf
49 | sed -i "s#LogFile=/tmp/zabbix_proxy.log#LogFile=/tmp/zabbix_proxy2.log#g" ./conf/zabbix_proxy2.conf
50 | sed -i 's#DBName=zabbix_proxy#DBName=/tmp/proxy1.db#' ./conf/zabbix_proxy1.conf
51 | sed -i 's#DBName=zabbix_proxy#DBName=/tmp/proxy2.db#' ./conf/zabbix_proxy2.conf
52 | echo -e "PidFile=/tmp/zabbix_proxy1/zabbix_proxy1.pid\n" >> ./conf/zabbix_proxy1.conf
53 | echo -e "PidFile=/tmp/zabbix_proxy2/zabbix_proxy2.pid\n" >> ./conf/zabbix_proxy2.conf
54 | echo -e "SocketDir=/tmp/zabbix_proxy1\n" >> ./conf/zabbix_proxy1.conf
55 | echo -e "SocketDir=/tmp/zabbix_proxy2\n" >> ./conf/zabbix_proxy2.conf
56 | echo -e "ListenPort=10061\n" >> ./conf/zabbix_proxy1.conf
57 | echo -e "ListenPort=10062\n" >> ./conf/zabbix_proxy2.conf
58 | sudo chown -R zabbix:zabbix /tmp/zabbix_proxy1/
59 | sudo chown -R zabbix:zabbix /tmp/zabbix_proxy2/
60 | cd ui
61 | sudo rm /var/www/html/index.html
62 | sudo cp -a . /var/www/html/
63 | sudo cp $WORKDIR/${{ env.CONFIG_PATH }}/zabbix.conf.php /var/www/html/conf/
64 | sudo cp $WORKDIR/${{ env.CONFIG_PATH }}/pg_hba.conf /etc/postgresql/16/main/pg_hba.conf
65 | sudo chown -R www-data:www-data /var/www/html/
66 | sudo sed -i "s/post_max_size = 8M/post_max_size = 16M/g" /etc/php/8.3/apache2/php.ini
67 | sudo sed -i "s/max_execution_time = 30/max_execution_time = 300/g" /etc/php/8.3/apache2/php.ini
68 | sudo sed -i "s/max_input_time = 60/max_input_time = 300/g" /etc/php/8.3/apache2/php.ini
69 | sudo locale-gen en_US.UTF-8
70 | sudo update-locale
71 | - name: Prepare environment
72 | run: |
73 | sudo mkdir -p /var/run/postgresql/16-main.pg_stat_tmp
74 | sudo touch /var/run/postgresql/16-main.pg_stat_tmp/global.tmp
75 | sudo chmod 0777 /var/run/postgresql/16-main.pg_stat_tmp/global.tmp
76 | (sudo -u postgres /usr/lib/postgresql/16/bin/postgres -D /var/lib/postgresql/16/main -c config_file=/etc/postgresql/16/main/postgresql.conf)&
77 | sleep 5
78 | cd /tmp/zabbix-branch/database/postgresql
79 | sudo -u postgres createuser zabbix
80 | sudo -u postgres createdb -O zabbix -E Unicode -T template0 zabbix
81 | cat schema.sql | sudo -u zabbix psql zabbix
82 | cat images.sql | sudo -u zabbix psql zabbix
83 | cat data.sql | sudo -u zabbix psql zabbix
84 | sudo apache2ctl start
85 | - name: Start Zabbix server
86 | run: |
87 | cd /tmp/zabbix-branch
88 | sudo ./src/zabbix_server/zabbix_server -c ./conf/zabbix_server.conf
89 | - name: Start Zabbix proxies
90 | continue-on-error: true
91 | run: |
92 | cd /tmp/zabbix-branch
93 | sudo ./src/zabbix_proxy/zabbix_proxy -c ./conf/zabbix_proxy1.conf
94 | sudo ./src/zabbix_proxy/zabbix_proxy -c ./conf/zabbix_proxy2.conf
95 | - name: Start Zabbix agent
96 | run: |
97 | cd /tmp/zabbix-branch
98 | sudo ./src/zabbix_agent/zabbix_agentd -c ./conf/zabbix_agentd.conf
99 | - name: Install python3
100 | run: |
101 | sudo apt-get install -y python3 python3-pip python-is-python3
102 | pip install -r ./requirements.txt
103 | - name: Wait for Zabbix API
104 | run: |
105 | python ./.github/scripts/wait_instance_zabbix.py
106 | - name: Compatibility test
107 | continue-on-error: true
108 | run: |
109 | python ./.github/scripts/$TEST_FILE 2>/tmp/compatibility.log >/dev/null
110 | - name: Send report
111 | env:
112 | TBOT_TOKEN: ${{ secrets.TBOT_TOKEN }}
113 | TBOT_CHAT: ${{ vars.TBOT_CHAT }}
114 | SUBJECT: Compatibility with Zabbix ${{ env.ZABBIX_VERSION }} FAIL
115 | run: |
116 | tail -n1 /tmp/compatibility.log | grep "OK" 1>/dev/null || tail /tmp/compatibility.log | python ./.github/scripts/telegram_msg.py | exit 1
117 |
--------------------------------------------------------------------------------
/.github/workflows/compatibility_72.yaml:
--------------------------------------------------------------------------------
1 | name: zabbix_72
2 | run-name: Compatibility with Zabbix 7.2 test
3 |
4 | on:
5 | push:
6 | branches: [main]
7 | pull_request:
8 | branches: [main]
9 | workflow_dispatch:
10 |
11 | env:
12 | ZABBIX_VERSION: '7.2'
13 | ZABBIX_BRANCH: release/$ZABBIX_VERSION
14 | CONFIG_PATH: .github/configs/
15 | TEST_FILE: compatibility_api_test_7.py
16 |
17 | jobs:
18 | compatibility:
19 | name: Compatibility test
20 | runs-on: ubuntu-24.04
21 |
22 | steps:
23 | - uses: actions/checkout@v4
24 | - name: Install packages
25 | run: |
26 | sudo apt update && sudo apt install -y git sudo gcc make automake pkg-config postgresql-16 libpostgresql-ocaml-dev libxml2-dev libpcre3-dev libevent-dev apache2 libapache2-mod-php php8.3-pgsql php8.3-bcmath php8.3-xml php8.3-gd php8.3-ldap php8.3-mbstring libzip-dev
27 | - name: Build from sources
28 | run: |
29 | WORKDIR=$(pwd)
30 | cd /tmp/
31 | sudo addgroup --system --quiet zabbix
32 | sudo adduser --quiet --system --disabled-login --ingroup zabbix --home /var/lib/zabbix --no-create-home zabbix
33 | git -c advice.detachedHead=false clone https://git.zabbix.com/scm/zbx/zabbix.git --branch ${{ env.ZABBIX_BRANCH }} --depth 1 --single-branch /tmp/zabbix-branch
34 | cd /tmp/zabbix-branch
35 | ./bootstrap.sh
36 | ./configure --enable-server --enable-agent --with-postgresql
37 | sudo make dbschema_postgresql
38 | sudo make
39 | echo -e "CacheUpdateFrequency=1\n" >> ./conf/zabbix_server.conf
40 | ./configure --enable-proxy --with-sqlite3
41 | sudo make
42 | mkdir /tmp/zabbix_proxy1/
43 | mkdir /tmp/zabbix_proxy2/
44 | cp ./conf/zabbix_proxy.conf ./conf/zabbix_proxy1.conf
45 | mv ./conf/zabbix_proxy.conf ./conf/zabbix_proxy2.conf
46 | sed -i "s/Hostname=Zabbix proxy/Hostname=CompatibilitySenderTest_proxy1/g" ./conf/zabbix_proxy1.conf
47 | sed -i "s/Hostname=Zabbix proxy/Hostname=CompatibilitySenderTest_proxy2/g" ./conf/zabbix_proxy2.conf
48 | sed -i "s#LogFile=/tmp/zabbix_proxy.log#LogFile=/tmp/zabbix_proxy1.log#g" ./conf/zabbix_proxy1.conf
49 | sed -i "s#LogFile=/tmp/zabbix_proxy.log#LogFile=/tmp/zabbix_proxy2.log#g" ./conf/zabbix_proxy2.conf
50 | sed -i 's#DBName=zabbix_proxy#DBName=/tmp/proxy1.db#' ./conf/zabbix_proxy1.conf
51 | sed -i 's#DBName=zabbix_proxy#DBName=/tmp/proxy2.db#' ./conf/zabbix_proxy2.conf
52 | echo -e "PidFile=/tmp/zabbix_proxy1/zabbix_proxy1.pid\n" >> ./conf/zabbix_proxy1.conf
53 | echo -e "PidFile=/tmp/zabbix_proxy2/zabbix_proxy2.pid\n" >> ./conf/zabbix_proxy2.conf
54 | echo -e "SocketDir=/tmp/zabbix_proxy1\n" >> ./conf/zabbix_proxy1.conf
55 | echo -e "SocketDir=/tmp/zabbix_proxy2\n" >> ./conf/zabbix_proxy2.conf
56 | echo -e "ListenPort=10061\n" >> ./conf/zabbix_proxy1.conf
57 | echo -e "ListenPort=10062\n" >> ./conf/zabbix_proxy2.conf
58 | sudo chown -R zabbix:zabbix /tmp/zabbix_proxy1/
59 | sudo chown -R zabbix:zabbix /tmp/zabbix_proxy2/
60 | cd ui
61 | sudo rm /var/www/html/index.html
62 | sudo cp -a . /var/www/html/
63 | sudo cp $WORKDIR/${{ env.CONFIG_PATH }}/zabbix.conf.php /var/www/html/conf/
64 | sudo cp $WORKDIR/${{ env.CONFIG_PATH }}/pg_hba.conf /etc/postgresql/16/main/pg_hba.conf
65 | sudo chown -R www-data:www-data /var/www/html/
66 | sudo sed -i "s/post_max_size = 8M/post_max_size = 16M/g" /etc/php/8.3/apache2/php.ini
67 | sudo sed -i "s/max_execution_time = 30/max_execution_time = 300/g" /etc/php/8.3/apache2/php.ini
68 | sudo sed -i "s/max_input_time = 60/max_input_time = 300/g" /etc/php/8.3/apache2/php.ini
69 | sudo locale-gen en_US.UTF-8
70 | sudo update-locale
71 | - name: Prepare environment
72 | run: |
73 | sudo mkdir -p /var/run/postgresql/16-main.pg_stat_tmp
74 | sudo touch /var/run/postgresql/16-main.pg_stat_tmp/global.tmp
75 | sudo chmod 0777 /var/run/postgresql/16-main.pg_stat_tmp/global.tmp
76 | (sudo -u postgres /usr/lib/postgresql/16/bin/postgres -D /var/lib/postgresql/16/main -c config_file=/etc/postgresql/16/main/postgresql.conf)&
77 | sleep 5
78 | cd /tmp/zabbix-branch/database/postgresql
79 | sudo -u postgres createuser zabbix
80 | sudo -u postgres createdb -O zabbix -E Unicode -T template0 zabbix
81 | cat schema.sql | sudo -u zabbix psql zabbix
82 | cat images.sql | sudo -u zabbix psql zabbix
83 | cat data.sql | sudo -u zabbix psql zabbix
84 | sudo apache2ctl start
85 | - name: Start Zabbix server
86 | run: |
87 | cd /tmp/zabbix-branch
88 | sudo ./src/zabbix_server/zabbix_server -c ./conf/zabbix_server.conf
89 | - name: Start Zabbix proxies
90 | continue-on-error: true
91 | run: |
92 | cd /tmp/zabbix-branch
93 | sudo ./src/zabbix_proxy/zabbix_proxy -c ./conf/zabbix_proxy1.conf
94 | sudo ./src/zabbix_proxy/zabbix_proxy -c ./conf/zabbix_proxy2.conf
95 | - name: Start Zabbix agent
96 | run: |
97 | cd /tmp/zabbix-branch
98 | sudo ./src/zabbix_agent/zabbix_agentd -c ./conf/zabbix_agentd.conf
99 | - name: Install python3
100 | run: |
101 | sudo apt-get install -y python3 python3-pip python-is-python3
102 | pip install -r ./requirements.txt
103 | - name: Wait for Zabbix API
104 | run: |
105 | python ./.github/scripts/wait_instance_zabbix.py
106 | - name: Compatibility test
107 | continue-on-error: true
108 | run: |
109 | python ./.github/scripts/$TEST_FILE 2>/tmp/compatibility.log >/dev/null
110 | - name: Send report
111 | env:
112 | TBOT_TOKEN: ${{ secrets.TBOT_TOKEN }}
113 | TBOT_CHAT: ${{ vars.TBOT_CHAT }}
114 | SUBJECT: Compatibility with Zabbix ${{ env.ZABBIX_VERSION }} FAIL
115 | run: |
116 | tail -n1 /tmp/compatibility.log | grep "OK" 1>/dev/null || tail /tmp/compatibility.log | python ./.github/scripts/telegram_msg.py | exit 1
117 |
--------------------------------------------------------------------------------
/.github/workflows/compatibility_latest.yaml:
--------------------------------------------------------------------------------
1 | name: zabbix_latest
2 | run-name: Compatibility with the latest Zabbix version test
3 | on:
4 | schedule:
5 | - cron: "0 1 * * *"
6 | workflow_dispatch:
7 |
8 | env:
9 | ZABBIX_VERSION: 'latest'
10 | ZABBIX_BRANCH: master
11 | CONFIG_PATH: .github/configs/
12 | TEST_FILE: compatibility_api_test_latest.py
13 |
14 | jobs:
15 | compatibility:
16 | name: Compatibility test
17 | runs-on: ubuntu-latest
18 |
19 | steps:
20 | - uses: actions/checkout@v4
21 | - name: Install packages
22 | run: |
23 | sudo apt update && sudo apt install -y git sudo gcc make automake pkg-config postgresql-16 libpostgresql-ocaml-dev libxml2-dev libpcre3-dev libevent-dev apache2 libapache2-mod-php php8.3-pgsql php8.3-bcmath php8.3-xml php8.3-gd php8.3-ldap php8.3-mbstring libzip-dev
24 | - name: Build from sources
25 | run: |
26 | WORKDIR=$(pwd)
27 | cd /tmp/
28 | sudo addgroup --system --quiet zabbix
29 | sudo adduser --quiet --system --disabled-login --ingroup zabbix --home /var/lib/zabbix --no-create-home zabbix
30 | git -c advice.detachedHead=false clone https://git.zabbix.com/scm/zbx/zabbix.git --branch ${{ env.ZABBIX_BRANCH }} --depth 1 --single-branch /tmp/zabbix-branch
31 | cd /tmp/zabbix-branch
32 | ./bootstrap.sh
33 | ./configure --enable-server --enable-agent --with-postgresql
34 | sudo make dbschema_postgresql
35 | sudo make
36 | echo -e "CacheUpdateFrequency=1\n" >> ./conf/zabbix_server.conf
37 | ./configure --enable-proxy --with-sqlite3
38 | sudo make
39 | mkdir /tmp/zabbix_proxy1/
40 | mkdir /tmp/zabbix_proxy2/
41 | cp ./conf/zabbix_proxy.conf ./conf/zabbix_proxy1.conf
42 | mv ./conf/zabbix_proxy.conf ./conf/zabbix_proxy2.conf
43 | sed -i "s/Hostname=Zabbix proxy/Hostname=CompatibilitySenderTest_proxy1/g" ./conf/zabbix_proxy1.conf
44 | sed -i "s/Hostname=Zabbix proxy/Hostname=CompatibilitySenderTest_proxy2/g" ./conf/zabbix_proxy2.conf
45 | sed -i "s#LogFile=/tmp/zabbix_proxy.log#LogFile=/tmp/zabbix_proxy1.log#g" ./conf/zabbix_proxy1.conf
46 | sed -i "s#LogFile=/tmp/zabbix_proxy.log#LogFile=/tmp/zabbix_proxy2.log#g" ./conf/zabbix_proxy2.conf
47 | sed -i 's#DBName=zabbix_proxy#DBName=/tmp/proxy1.db#' ./conf/zabbix_proxy1.conf
48 | sed -i 's#DBName=zabbix_proxy#DBName=/tmp/proxy2.db#' ./conf/zabbix_proxy2.conf
49 | echo -e "PidFile=/tmp/zabbix_proxy1/zabbix_proxy1.pid\n" >> ./conf/zabbix_proxy1.conf
50 | echo -e "PidFile=/tmp/zabbix_proxy2/zabbix_proxy2.pid\n" >> ./conf/zabbix_proxy2.conf
51 | echo -e "SocketDir=/tmp/zabbix_proxy1\n" >> ./conf/zabbix_proxy1.conf
52 | echo -e "SocketDir=/tmp/zabbix_proxy2\n" >> ./conf/zabbix_proxy2.conf
53 | echo -e "ListenPort=10061\n" >> ./conf/zabbix_proxy1.conf
54 | echo -e "ListenPort=10062\n" >> ./conf/zabbix_proxy2.conf
55 | sudo chown -R zabbix:zabbix /tmp/zabbix_proxy1/
56 | sudo chown -R zabbix:zabbix /tmp/zabbix_proxy2/
57 | cd ui
58 | sudo rm /var/www/html/index.html
59 | sudo cp -a . /var/www/html/
60 | sudo cp $WORKDIR/${{ env.CONFIG_PATH }}/zabbix.conf.php /var/www/html/conf/
61 | sudo cp $WORKDIR/${{ env.CONFIG_PATH }}/pg_hba.conf /etc/postgresql/16/main/pg_hba.conf
62 | sudo chown -R www-data:www-data /var/www/html/
63 | sudo sed -i "s/post_max_size = 8M/post_max_size = 16M/g" /etc/php/8.3/apache2/php.ini
64 | sudo sed -i "s/max_execution_time = 30/max_execution_time = 300/g" /etc/php/8.3/apache2/php.ini
65 | sudo sed -i "s/max_input_time = 60/max_input_time = 300/g" /etc/php/8.3/apache2/php.ini
66 | sudo locale-gen en_US.UTF-8
67 | sudo update-locale
68 | - name: Prepare environment
69 | run: |
70 | sudo mkdir -p /var/run/postgresql/16-main.pg_stat_tmp
71 | sudo touch /var/run/postgresql/16-main.pg_stat_tmp/global.tmp
72 | sudo chmod 0777 /var/run/postgresql/16-main.pg_stat_tmp/global.tmp
73 | (sudo -u postgres /usr/lib/postgresql/16/bin/postgres -D /var/lib/postgresql/16/main -c config_file=/etc/postgresql/16/main/postgresql.conf)&
74 | sleep 5
75 | cd /tmp/zabbix-branch/database/postgresql
76 | sudo -u postgres createuser zabbix
77 | sudo -u postgres createdb -O zabbix -E Unicode -T template0 zabbix
78 | cat schema.sql | sudo -u zabbix psql zabbix
79 | cat images.sql | sudo -u zabbix psql zabbix
80 | cat data.sql | sudo -u zabbix psql zabbix
81 | sudo apache2ctl start
82 | - name: Start Zabbix server
83 | run: |
84 | cd /tmp/zabbix-branch
85 | sudo ./src/zabbix_server/zabbix_server -c ./conf/zabbix_server.conf
86 | - name: Start Zabbix proxies
87 | continue-on-error: true
88 | run: |
89 | cd /tmp/zabbix-branch
90 | sudo ./src/zabbix_proxy/zabbix_proxy -c ./conf/zabbix_proxy1.conf
91 | sudo ./src/zabbix_proxy/zabbix_proxy -c ./conf/zabbix_proxy2.conf
92 | - name: Start Zabbix agent
93 | run: |
94 | cd /tmp/zabbix-branch
95 | sudo ./src/zabbix_agent/zabbix_agentd -c ./conf/zabbix_agentd.conf
96 | - name: Install python3
97 | run: |
98 | sudo apt-get install -y python3 python3-pip python-is-python3
99 | pip install -r ./requirements.txt
100 | - name: Wait for Zabbix API
101 | run: |
102 | python ./.github/scripts/wait_instance_zabbix.py
103 | - name: Print Zabbix version
104 | continue-on-error: true
105 | run: |
106 | grep -Po "(?<=Changes for ).*$" /tmp/zabbix-branch/ChangeLog 2>/dev/null | head -n1
107 | - name: Compatibility test
108 | continue-on-error: true
109 | run: |
110 | python ./.github/scripts/$TEST_FILE 2>/tmp/compatibility.log >/dev/null
111 | - name: Send report
112 | env:
113 | TBOT_TOKEN: ${{ secrets.TBOT_TOKEN }}
114 | TBOT_CHAT: ${{ vars.TBOT_CHAT }}
115 | SUBJECT: Compatibility with Zabbix ${{ env.ZABBIX_VERSION }} FAIL
116 | run: |
117 | tail -n1 /tmp/compatibility.log | grep "OK" 1>/dev/null || tail /tmp/compatibility.log | python ./.github/scripts/telegram_msg.py | exit 1
118 |
--------------------------------------------------------------------------------
/.github/workflows/coverage.yaml:
--------------------------------------------------------------------------------
1 | name: coverage
2 | run-name: Check test coverage
3 |
4 | on:
5 | push:
6 | branches: [main]
7 | pull_request:
8 | branches: [main]
9 | workflow_dispatch:
10 |
11 | jobs:
12 | coverage:
13 | name: Check coverage
14 | runs-on: ubuntu-latest
15 |
16 | steps:
17 | - uses: actions/checkout@v4
18 | - name: Set up Python 3.10
19 | uses: actions/setup-python@v5
20 | with:
21 | python-version: "3.10"
22 | - name: Install dependencies
23 | run: |
24 | python -m pip install --upgrade pip
25 | pip install -r ./requirements.txt
26 | pip install coverage
27 | - name: Test with coverage
28 | run: |
29 | coverage run -m unittest discover -s ./tests -p 'test_*.py'
30 | coverage report
31 |
--------------------------------------------------------------------------------
/.github/workflows/depricated_tests.yaml:
--------------------------------------------------------------------------------
1 | name: depricated_tests
2 | run-name: Tests for deprecated features
3 |
4 | on:
5 | push:
6 | branches: [main]
7 | pull_request:
8 | branches: [main]
9 | workflow_dispatch:
10 |
11 | env:
12 | ZABBIX_VERSION: '7.0'
13 | ZABBIX_BRANCH: release/$ZABBIX_VERSION
14 | CONFIG_PATH: .github/configs/
15 | TEST_FILE: depricated_tests.py
16 |
17 | jobs:
18 | depricated-tests:
19 | name: Depricated tests
20 | runs-on: ubuntu-22.04
21 |
22 | steps:
23 | - uses: actions/checkout@v3
24 | - name: Install packages
25 | run: |
26 | sudo apt update && sudo apt install -y git sudo nginx gcc make automake pkg-config postgresql-14 libpostgresql-ocaml-dev libxml2-dev libpcre3-dev libevent-dev apache2 libapache2-mod-php php8.1-pgsql php8.1-bcmath php8.1-xml php8.1-gd php8.1-ldap php8.1-mbstring libzip-dev
27 | - name: Build from sources
28 | run: |
29 | WORKDIR=$(pwd)
30 | cd /tmp/
31 | git -c advice.detachedHead=false clone https://git.zabbix.com/scm/zbx/zabbix.git --branch ${{ env.ZABBIX_BRANCH }} --depth 1 --single-branch /tmp/zabbix-branch
32 | cd /tmp/zabbix-branch
33 | ./bootstrap.sh
34 | ./configure --enable-server --with-postgresql
35 | sudo make dbschema_postgresql
36 | echo -e "CacheUpdateFrequency=1\n" >> ./conf/zabbix_server.conf
37 | sudo mkdir -p /etc/nginx/ssl/
38 | sudo cp $WORKDIR/${{ env.CONFIG_PATH }}/.htpasswd /etc/nginx/.htpasswd
39 | sudo cp $WORKDIR/${{ env.CONFIG_PATH }}/default.conf /etc/nginx/sites-enabled/default
40 | sudo openssl req -x509 -nodes -days 1 -newkey rsa:2048 -keyout /etc/nginx/ssl/nginx.key -out /etc/nginx/ssl/nginx.crt -config $WORKDIR/${{ env.CONFIG_PATH }}/nginx.cnf
41 | sudo chown -R www-data:www-data /etc/nginx/
42 | cd ui
43 | sudo rm /var/www/html/index.html
44 | sudo cp -a . /var/www/html/
45 | sudo cp $WORKDIR/${{ env.CONFIG_PATH }}/zabbix.conf.php /var/www/html/conf/
46 | sudo cp $WORKDIR/${{ env.CONFIG_PATH }}/pg_hba.conf /etc/postgresql/14/main/pg_hba.conf
47 | sudo chown -R www-data:www-data /var/www/html/
48 | sudo sed -i "s/post_max_size = 8M/post_max_size = 16M/g" /etc/php/8.1/apache2/php.ini
49 | sudo sed -i "s/max_execution_time = 30/max_execution_time = 300/g" /etc/php/8.1/apache2/php.ini
50 | sudo sed -i "s/max_input_time = 60/max_input_time = 300/g" /etc/php/8.1/apache2/php.ini
51 | sudo sed -i "s/Listen 80/Listen 8080/g" /etc/apache2/ports.conf
52 | sudo sed -i "s///g" /etc/apache2/sites-enabled/000-default.conf
53 | sudo locale-gen en_US.UTF-8
54 | sudo update-locale
55 | - name: Prepare environment
56 | run: |
57 | sudo addgroup --system --quiet zabbix
58 | sudo adduser --quiet --system --disabled-login --ingroup zabbix --home /var/lib/zabbix --no-create-home zabbix
59 | sudo mkdir -p /var/run/postgresql/14-main.pg_stat_tmp
60 | sudo touch /var/run/postgresql/14-main.pg_stat_tmp/global.tmp
61 | sudo chmod 0777 /var/run/postgresql/14-main.pg_stat_tmp/global.tmp
62 | (sudo -u postgres /usr/lib/postgresql/14/bin/postgres -D /var/lib/postgresql/14/main -c config_file=/etc/postgresql/14/main/postgresql.conf)&
63 | sleep 5
64 | cd /tmp/zabbix-branch/database/postgresql
65 | sudo -u postgres createuser zabbix
66 | sudo -u postgres createdb -O zabbix -E Unicode -T template0 zabbix
67 | cat schema.sql | sudo -u zabbix psql zabbix
68 | cat images.sql | sudo -u zabbix psql zabbix
69 | cat data.sql | sudo -u zabbix psql zabbix
70 | - name: Start Apache & Nginx
71 | run: |
72 | sudo apache2ctl start
73 | sudo nginx -g "daemon on; master_process on;"
74 | - name: Install python3
75 | run: |
76 | sudo apt-get install -y python3 python3-pip python-is-python3
77 | pip install -r ./requirements.txt
78 | - name: Run tests
79 | continue-on-error: true
80 | run: |
81 | sleep 5
82 | python ./.github/scripts/$TEST_FILE 2>/tmp/depricated.log >/dev/null
83 | - name: Send report
84 | env:
85 | TBOT_TOKEN: ${{ secrets.TBOT_TOKEN }}
86 | TBOT_CHAT: ${{ vars.TBOT_CHAT }}
87 | SUBJECT: Zabbix API depricated tests FAIL
88 | run: |
89 | tail -n1 /tmp/depricated.log | grep "OK" 1>/dev/null || tail /tmp/depricated.log | python ./.github/scripts/telegram_msg.py | exit 1
90 |
--------------------------------------------------------------------------------
/.github/workflows/integration_api.yaml:
--------------------------------------------------------------------------------
1 | name: api
2 | run-name: Zabbix API integration test
3 |
4 | on:
5 | push:
6 | branches: [main]
7 | paths:
8 | - '**api.py'
9 | pull_request:
10 | branches: [main]
11 | paths:
12 | - '**api.py'
13 | workflow_dispatch:
14 |
15 | env:
16 | ZABBIX_BRANCH: master
17 | CONFIG_PATH: .github/configs/
18 | SYNC_FILE: integration_api_test.py
19 | ASYNC_FILE: integration_aioapi_test.py
20 |
21 | jobs:
22 | integration:
23 | name: Integration test
24 | runs-on: ubuntu-latest
25 |
26 | steps:
27 | - uses: actions/checkout@v4
28 | - name: Install packages
29 | run: |
30 | sudo apt update && sudo apt install -y git sudo gcc make automake pkg-config postgresql-14 libpostgresql-ocaml-dev libxml2-dev libpcre3-dev libevent-dev apache2 libapache2-mod-php php8.1-pgsql php8.1-bcmath php8.1-xml php8.1-gd php8.1-ldap php8.1-mbstring libzip-dev
31 | - name: Build from sources
32 | run: |
33 | WORKDIR=$(pwd)
34 | cd /tmp/
35 | git -c advice.detachedHead=false clone https://git.zabbix.com/scm/zbx/zabbix.git --branch ${{ env.ZABBIX_BRANCH }} --depth 1 --single-branch /tmp/zabbix-branch
36 | cd /tmp/zabbix-branch
37 | ./bootstrap.sh
38 | ./configure --enable-server --with-postgresql
39 | sudo make dbschema_postgresql
40 | echo -e "CacheUpdateFrequency=1\n" >> ./conf/zabbix_server.conf
41 | cd ui
42 | sudo rm /var/www/html/index.html
43 | sudo cp -a . /var/www/html/
44 | sudo cp $WORKDIR/${{ env.CONFIG_PATH }}/zabbix.conf.php /var/www/html/conf/
45 | sudo cp $WORKDIR/${{ env.CONFIG_PATH }}/pg_hba.conf /etc/postgresql/14/main/pg_hba.conf
46 | sudo chown -R www-data:www-data /var/www/html/
47 | sudo sed -i "s/post_max_size = 8M/post_max_size = 16M/g" /etc/php/8.1/apache2/php.ini
48 | sudo sed -i "s/max_execution_time = 30/max_execution_time = 300/g" /etc/php/8.1/apache2/php.ini
49 | sudo sed -i "s/max_input_time = 60/max_input_time = 300/g" /etc/php/8.1/apache2/php.ini
50 | sudo locale-gen en_US.UTF-8
51 | sudo update-locale
52 | - name: Prepare environment
53 | run: |
54 | sudo addgroup --system --quiet zabbix
55 | sudo adduser --quiet --system --disabled-login --ingroup zabbix --home /var/lib/zabbix --no-create-home zabbix
56 | sudo mkdir -p /var/run/postgresql/14-main.pg_stat_tmp
57 | sudo touch /var/run/postgresql/14-main.pg_stat_tmp/global.tmp
58 | sudo chmod 0777 /var/run/postgresql/14-main.pg_stat_tmp/global.tmp
59 | (sudo -u postgres /usr/lib/postgresql/14/bin/postgres -D /var/lib/postgresql/14/main -c config_file=/etc/postgresql/14/main/postgresql.conf)&
60 | sleep 1
61 | cd /tmp/zabbix-branch/database/postgresql
62 | sudo -u postgres createuser zabbix
63 | sudo -u postgres createdb -O zabbix -E Unicode -T template0 zabbix
64 | cat schema.sql | sudo -u zabbix psql zabbix
65 | cat images.sql | sudo -u zabbix psql zabbix
66 | cat data.sql | sudo -u zabbix psql zabbix
67 | sudo apache2ctl start
68 | - name: Install python3
69 | run: |
70 | sudo apt-get install -y python3 python3-pip python-is-python3
71 | pip install -r ./requirements.txt
72 | - name: Wait for Zabbix API
73 | run: |
74 | python ./.github/scripts/wait_instance_zabbix.py
75 | - name: Print Zabbix version
76 | continue-on-error: true
77 | run: |
78 | grep -Po "(?<=Changes for ).*$" /tmp/zabbix-branch/ChangeLog 2>/dev/null | head -n1
79 | - name: Integration synchronous test
80 | continue-on-error: true
81 | run: |
82 | python ./.github/scripts/$SYNC_FILE 2>/tmp/integration_sync.log >/dev/null
83 | - name: Integration asynchronous test
84 | continue-on-error: true
85 | run: |
86 | python ./.github/scripts/$ASYNC_FILE 2>/tmp/integration_async.log >/dev/null
87 | - name: Send report
88 | env:
89 | TBOT_TOKEN: ${{ secrets.TBOT_TOKEN }}
90 | TBOT_CHAT: ${{ vars.TBOT_CHAT }}
91 | SUBJECT: Zabbix API integration test FAIL
92 | run: |
93 | err=0
94 | tail -n1 /tmp/integration_sync.log | grep "OK" 1>/dev/null || tail /tmp/integration_sync.log | python ./.github/scripts/telegram_msg.py 2>/dev/null | err=1
95 | tail -n1 /tmp/integration_async.log | grep "OK" 1>/dev/null || tail /tmp/integration_async.log | python ./.github/scripts/telegram_msg.py 2>/dev/null | err=1
96 | if [ "$err" = 1 ]; then exit 1; fi
97 |
98 |
--------------------------------------------------------------------------------
/.github/workflows/integration_getter.yaml:
--------------------------------------------------------------------------------
1 | name: get
2 | run-name: Zabbix get integration test
3 |
4 | on:
5 | push:
6 | branches: [main]
7 | paths:
8 | - '**getter.py'
9 | pull_request:
10 | branches: [main]
11 | paths:
12 | - '**getter.py'
13 | workflow_dispatch:
14 |
15 | env:
16 | ZABBIX_VERSION: '6.0'
17 | ZABBIX_BRANCH: master
18 | CONFIG_PATH: .github/configs/
19 | SYNC_FILE: integration_getter_test.py
20 | ASYNC_FILE: integration_aiogetter_test.py
21 |
22 | jobs:
23 | integration:
24 | name: Integration test
25 | runs-on: ubuntu-22.04
26 |
27 | steps:
28 | - uses: actions/checkout@v4
29 | - name: Install packages
30 | run: |
31 | sudo wget https://repo.zabbix.com/zabbix/${{ env.ZABBIX_VERSION }}/ubuntu/pool/main/z/zabbix-release/zabbix-release_${{ env.ZABBIX_VERSION }}-4+ubuntu22.04_all.deb
32 | sudo dpkg -i zabbix-release_${{ env.ZABBIX_VERSION }}-4+ubuntu22.04_all.deb
33 | sudo apt update && sudo apt install -y zabbix-agent
34 | - name: Prepare environment
35 | run: |
36 | sudo mkdir -p /var/log/zabbix/
37 | sudo chown -R zabbix. /var/log/zabbix/
38 | - name: Start Zabbix agent
39 | run: |
40 | sudo zabbix_agentd -c /etc/zabbix/zabbix_agentd.conf
41 | - name: Install python3
42 | run: |
43 | sudo apt-get install -y python3 python3-pip python-is-python3
44 | pip install -r ./requirements.txt
45 | - name: Integration synchronous test
46 | continue-on-error: true
47 | run: |
48 | python ./.github/scripts/$SYNC_FILE 2>/tmp/integration_sync.log >/dev/null
49 | - name: Integration asynchronous test
50 | continue-on-error: true
51 | run: |
52 | python ./.github/scripts/$ASYNC_FILE 2>/tmp/integration_async.log >/dev/null
53 | - name: Send report
54 | env:
55 | TBOT_TOKEN: ${{ secrets.TBOT_TOKEN }}
56 | TBOT_CHAT: ${{ vars.TBOT_CHAT }}
57 | SUBJECT: Zabbix get integration test FAIL
58 | run: |
59 | err=0
60 | tail -n1 /tmp/integration_sync.log | grep "OK" 1>/dev/null || tail /tmp/integration_sync.log | python ./.github/scripts/telegram_msg.py 2>/dev/null | err=1
61 | tail -n1 /tmp/integration_async.log | grep "OK" 1>/dev/null || tail /tmp/integration_async.log | python ./.github/scripts/telegram_msg.py 2>/dev/null | err=1
62 | if [ "$err" = 1 ]; then exit 1; fi
63 |
--------------------------------------------------------------------------------
/.github/workflows/integration_sender.yaml:
--------------------------------------------------------------------------------
1 | name: sender
2 | run-name: Zabbix sender integration test
3 |
4 | on:
5 | push:
6 | branches: [main]
7 | paths:
8 | - '**sender.py'
9 | pull_request:
10 | branches: [main]
11 | paths:
12 | - '**sender.py'
13 | workflow_dispatch:
14 |
15 | env:
16 | ZABBIX_VERSION: '6.0'
17 | ZABBIX_BRANCH: master
18 | CONFIG_PATH: .github/configs/
19 | SYNC_FILE: integration_sender_test.py
20 | ASYNC_FILE: integration_aiosender_test.py
21 |
22 | jobs:
23 | integration:
24 | name: Integration test
25 | runs-on: ubuntu-22.04
26 |
27 | steps:
28 | - uses: actions/checkout@v4
29 | - name: Install packages
30 | run: |
31 | sudo wget https://repo.zabbix.com/zabbix/${{ env.ZABBIX_VERSION }}/ubuntu/pool/main/z/zabbix-release/zabbix-release_${{ env.ZABBIX_VERSION }}-4+ubuntu22.04_all.deb
32 | sudo dpkg -i zabbix-release_${{ env.ZABBIX_VERSION }}-4+ubuntu22.04_all.deb
33 | sudo apt update && sudo apt install -y zabbix-proxy-sqlite3
34 | - name: Prepare environment
35 | run: |
36 | sudo mkdir -p /var/log/zabbix/
37 | sudo chown -R zabbix. /var/log/zabbix/
38 | sudo sed -i 's#DBName=zabbix_proxy#DBName=/tmp/proxy.db#' /etc/zabbix/zabbix_proxy.conf
39 | - name: Start Zabbix proxy
40 | run: |
41 | sudo zabbix_proxy -c /etc/zabbix/zabbix_proxy.conf
42 | - name: Install python3
43 | run: |
44 | sudo apt-get install -y python3 python3-pip python-is-python3
45 | pip install -r ./requirements.txt
46 | - name: Integration synchronous test
47 | continue-on-error: true
48 | run: |
49 | python ./.github/scripts/$SYNC_FILE 2>/tmp/integration_sync.log >/dev/null
50 | - name: Integration asynchronous test
51 | continue-on-error: true
52 | run: |
53 | python ./.github/scripts/$ASYNC_FILE 2>/tmp/integration_async.log >/dev/null
54 | - name: Send report
55 | env:
56 | TBOT_TOKEN: ${{ secrets.TBOT_TOKEN }}
57 | TBOT_CHAT: ${{ vars.TBOT_CHAT }}
58 | SUBJECT: Zabbix sender integration test FAIL
59 | run: |
60 | err=0
61 | tail -n1 /tmp/integration_sync.log | grep "OK" 1>/dev/null || tail /tmp/integration_sync.log | python ./.github/scripts/telegram_msg.py 2>/dev/null | err=1
62 | tail -n1 /tmp/integration_async.log | grep "OK" 1>/dev/null || tail /tmp/integration_async.log | python ./.github/scripts/telegram_msg.py 2>/dev/null | err=1
63 | if [ "$err" = 1 ]; then exit 1; fi
64 |
--------------------------------------------------------------------------------
/.github/workflows/release.yaml:
--------------------------------------------------------------------------------
1 | name: release
2 | run-name: Release new version
3 |
4 | on:
5 | push:
6 | branches: [main]
7 | paths:
8 | - '**version.py'
9 | workflow_dispatch:
10 |
11 | jobs:
12 | release:
13 | name: Release new version
14 | runs-on: ubuntu-latest
15 |
16 | steps:
17 | - uses: actions/checkout@v4
18 | - uses: actions/setup-python@v5
19 | with:
20 | python-version: '3.10'
21 | - name: Get pip cache
22 | id: pip-cache
23 | run: |
24 | python -c "from pip._internal.locations import USER_CACHE_DIR; print('dir=' + USER_CACHE_DIR)" >> $GITHUB_OUTPUT
25 | - uses: actions/cache@v3
26 | with:
27 | path: ${{ steps.pip-cache.outputs.dir }}
28 | key: ${{ runner.os }}-pip
29 | - name: Install python requirements
30 | run: |
31 | python -m pip install --upgrade pip
32 | pip install setuptools wheel markdown
33 | - name: Set version env
34 | run: |
35 | echo "LIBRARY_VERSION=$(python -c "import sys; sys.path.append('.'); from zabbix_utils.version import __version__; print(__version__)")" >> $GITHUB_ENV
36 | - name: Build packages
37 | run: |
38 | python setup.py sdist bdist_wheel
39 | - name: Add version tag
40 | uses: pkgdeps/git-tag-action@v2
41 | with:
42 | github_token: ${{ secrets.GITHUB_TOKEN }}
43 | github_repo: ${{ github.repository }}
44 | git_commit_sha: ${{ github.sha }}
45 | version: ${{ env.LIBRARY_VERSION }}
46 | git_tag_prefix: "v"
47 | - name: Archive built packages
48 | uses: actions/upload-artifact@v4
49 | with:
50 | name: Archive-v${{ env.LIBRARY_VERSION }}
51 | path: dist/*
52 | - name: Create release notes
53 | run: |
54 | grep -Pzo '(?s)^## \[${{ env.LIBRARY_VERSION }}\].*?(?=## \[)' CHANGELOG.md | sed '$ s/.$//' > RELEASE_NOTES.md
55 | sed -i 's/\[${{ env.LIBRARY_VERSION }}\]/Release [v${{ env.LIBRARY_VERSION }}]/' RELEASE_NOTES.md
56 | - name: Create release
57 | uses: ncipollo/release-action@v1
58 | env:
59 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
60 | with:
61 | name: v${{ env.LIBRARY_VERSION }}
62 | draft: true
63 | prerelease: false
64 | makeLatest: true
65 | skipIfReleaseExists: true
66 | replacesArtifacts: true
67 | removeArtifacts: true
68 | tag: "v${{ env.LIBRARY_VERSION }}"
69 | bodyFile: RELEASE_NOTES.md
70 | artifacts: dist/*
71 | - name: Send notification
72 | run: |
73 | python ./.github/scripts/release_notification.py
74 | working-directory: ./
75 | env:
76 | MAIL_SERVER: ${{ secrets.MAIL_SERVER }}
77 | MAIL_PORT: ${{ secrets.MAIL_PORT }}
78 | MAIL_USER: ${{ secrets.MAIL_USER }}
79 | MAIL_PASS: ${{ secrets.MAIL_PASS }}
80 | RELEASE_RECIPIENT_LIST: ${{ secrets.RELEASE_RECIPIENT_LIST }}
81 | LIBRARY_VERSION: ${{ env.LIBRARY_VERSION }}
82 | REPOSITORY: ${{ github.repository }}
83 |
--------------------------------------------------------------------------------
/.github/workflows/tests.yaml:
--------------------------------------------------------------------------------
1 | name: tests
2 | run-name: Library unit-tests on different platforms and versions
3 |
4 | on:
5 | push:
6 | branches: [main]
7 | pull_request:
8 | branches: [main]
9 | workflow_dispatch:
10 |
11 | jobs:
12 | unit-tests:
13 | strategy:
14 | matrix:
15 | python-version:
16 | - "3.8"
17 | - "3.9"
18 | - "3.10"
19 | - "3.11"
20 | - "3.12"
21 | platform:
22 | - ubuntu-latest
23 | - macos-latest
24 | - windows-latest
25 | runs-on: ${{ matrix.platform }}
26 | steps:
27 | - uses: actions/checkout@v4
28 | - name: Set up Python ${{ matrix.python-version }}
29 | uses: actions/setup-python@v5
30 | with:
31 | python-version: ${{ matrix.python-version }}
32 | - name: Install dependencies
33 | run: |
34 | python -m pip install --upgrade pip
35 | pip install -r ./requirements.txt
36 | pip install -r ./requirements-dev.txt
37 | pip install coverage
38 | - name: Lint with flake8
39 | run: |
40 | flake8 ./zabbix_utils/ --count --select=E9,F63,F7,F82 --show-source --statistics
41 | flake8 ./zabbix_utils/ --count --exit-zero --max-complexity=20 --max-line-length=127 --statistics
42 | - name: Test with unittest
43 | run: |
44 | python -m unittest discover -s ./tests -p 'test_*.py'
45 |
46 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Editors
2 | .vscode
3 | .idea
4 |
5 | # Compiled
6 | __pycache__/
7 | *.py[cod]
8 | *$py.class
9 |
10 | # Packaging
11 | build/
12 | develop-eggs/
13 | dist/
14 | *.egg-info/
15 |
16 | # Environments
17 | .env
18 | .venv
19 |
20 | # Coverage
21 | htmlcov/
22 | .coverage
23 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [2.0.2](https://github.com/zabbix/python-zabbix-utils/compare/v2.0.1...v2.0.2) (2024-12-12)
2 |
3 | ### Features:
4 |
5 | - added support for Zabbix 7.2
6 | - added support of proxy groups for Sender and AsyncSender
7 |
8 | ### Changes:
9 |
10 | - discontinued support for HTTP authentication for Zabbix 7.2 and newer
11 | - discontinued support for Zabbix 6.4
12 | - added examples of deleting items
13 | - added examples of how to clear item history
14 | - added examples of how to pass get request parameters
15 |
16 | ### Bug fixes:
17 |
18 | - fixed issue [#21](https://github.com/zabbix/python-zabbix-utils/issues/21) with non-obvious format of ID array passing
19 | - fixed issue [#26](https://github.com/zabbix/python-zabbix-utils/issues/26) with Sender and AsyncSender working with proxy groups
20 | - fixed small bugs and flaws
21 |
22 | ## [2.0.1](https://github.com/zabbix/python-zabbix-utils/compare/v2.0.0...v2.0.1) (2024-09-18)
23 |
24 | ### Features:
25 |
26 | - added ssl_context argument to ZabbixAPI to allow more flexible configuration of SSL connections
27 | - added support of SSL connection configuration to AsyncZabbixAPI
28 |
29 | ## [2.0.0](https://github.com/zabbix/python-zabbix-utils/compare/v1.1.1...v2.0.0) (2024-04-12)
30 |
31 | ### Features:
32 |
33 | - added asynchronous modules: AsyncZabbixAPI, AsyncSender, AsyncGetter
34 | - added examples of working with asynchronous modules
35 |
36 | ### Bug fixes:
37 |
38 | - fixed issue [#7](https://github.com/zabbix/python-zabbix-utils/issues/7) in examples of PSK using on Linux
39 | - fixed small bugs and flaws
40 |
41 | ## [1.1.1](https://github.com/zabbix/python-zabbix-utils/compare/v1.1.0...v1.1.1) (2024-03-06)
42 |
43 | ### Changes:
44 |
45 | - removed external requirements
46 |
47 | ## [1.1.0](https://github.com/zabbix/python-zabbix-utils/compare/v1.0.3...v1.1.0) (2024-01-23)
48 |
49 | ### Breaking Changes:
50 |
51 | - changed the format of the Sender response
52 | - changed the format of the Getter response
53 |
54 | ### Features:
55 |
56 | - implemented support for specifying Zabbix clusters in Sender
57 | - implemented pre-processing of the agent response
58 |
59 | ### Bug fixes:
60 |
61 | - fixed issue with hiding private (sensitive) fields in the log
62 | - fixed small bugs and flaws
63 |
64 | ## [1.0.3](https://github.com/zabbix/python-zabbix-utils/compare/v1.0.2...v1.0.3) (2024-01-09)
65 |
66 | ### Documentation
67 |
68 | - added support for Python 3.12
69 | - discontinued support for Python 3.7
70 |
71 | ### Bug fixes:
72 |
73 | - fixed issue with hiding private (sensitive) information in the log.
74 | - fixed small bugs and flaws.
75 |
76 | ## [1.0.2](https://github.com/zabbix/python-zabbix-utils/compare/v1.0.1...v1.0.2) (2023-12-15)
77 |
78 | ### Bug fixes:
79 |
80 | - added trailing underscores as workaround to use Python keywords as names of API object or method
81 | - changed TypeError to ValueError for the exception during version parsing.
82 | - fixed compression support for Sender and Getter.
83 | - made refactoring of some parts of the code.
84 | - fixed small bugs and flaws.
85 |
86 | ## [1.0.1](https://github.com/zabbix/python-zabbix-utils/compare/v1.0.0...v1.0.1) (2023-11-27)
87 |
88 | ### Bug fixes:
89 |
90 | - removed deprecated API fields from examples and README.
91 | - removed "Get started" section from README for PyPI.
92 | - fixed small flaws.
93 |
94 | ## [1.0.0](https://github.com/zabbix/python-zabbix-utils/tree/v1.0.0) (2023-11-17)
95 |
96 | Initial release
97 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (C) 2001-2023 Zabbix SIA
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to use,
8 | copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
9 | Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
18 | OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
20 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/examples/api/asynchronous/auth_by_token.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | import asyncio
7 | from zabbix_utils import AsyncZabbixAPI
8 |
9 |
10 | # Zabbix server URL or IP address.
11 | ZABBIX_SERVER = "127.0.0.1"
12 |
13 | # Use an authentication token generated via the web interface or
14 | # API instead of standard authentication by username and password.
15 | ZABBIX_TOKEN = "8jF7sGh2Rp4TlQ1ZmXo0uYv3Bc6AiD9E"
16 |
17 |
18 | async def main():
19 | """
20 | The main function to perform asynchronous tasks.
21 | """
22 |
23 | # Create an instance of the AsyncZabbixAPI class.
24 | api = AsyncZabbixAPI(ZABBIX_SERVER)
25 |
26 | # Authenticating with Zabbix API using the provided token.
27 | await api.login(token=ZABBIX_TOKEN)
28 |
29 | # Retrieve a list of users, including their user ID and name
30 | users = await api.user.get(
31 | output=['userid', 'name']
32 | )
33 |
34 | # Print the names of the retrieved users
35 | for user in users:
36 | print(user['name'])
37 |
38 | # Close asynchronous connection
39 | await api.logout()
40 |
41 | # Run the main coroutine
42 | asyncio.run(main())
43 |
--------------------------------------------------------------------------------
/examples/api/asynchronous/check_auth_state.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | import asyncio
7 | from zabbix_utils import AsyncZabbixAPI
8 |
9 |
10 | # Zabbix server URL or IP address
11 | ZABBIX_SERVER = "127.0.0.1"
12 |
13 | # Zabbix server authentication credentials
14 | ZABBIX_AUTH = {
15 | "user": "Admin", # Zabbix user name for authentication
16 | "password": "zabbix" # Zabbix user password for authentication
17 | }
18 |
19 |
20 | async def main():
21 | """
22 | The main function to perform asynchronous tasks.
23 | """
24 |
25 | # Create an instance of the AsyncZabbixAPI class
26 | api = AsyncZabbixAPI(ZABBIX_SERVER)
27 |
28 | # Authenticating with Zabbix API using the provided username and password.
29 | await api.login(**ZABBIX_AUTH)
30 |
31 | # Some actions when your session can be released
32 | # For example, api.logout()
33 |
34 | # Check if authentication is still valid
35 | if await api.check_auth():
36 | # Retrieve a list of hosts from the Zabbix server, including their host ID and name
37 | hosts = await api.host.get(
38 | output=['hostid', 'name']
39 | )
40 |
41 | # Print the names of the retrieved hosts
42 | for host in hosts:
43 | print(host['name'])
44 |
45 | # Logout to release the Zabbix API session and close asynchronous connection
46 | await api.logout()
47 |
48 | # Run the main coroutine
49 | asyncio.run(main())
50 |
--------------------------------------------------------------------------------
/examples/api/asynchronous/clear_history.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | import asyncio
7 | from zabbix_utils import AsyncZabbixAPI, APIRequestError
8 |
9 | # Zabbix server URL or IP address
10 | ZABBIX_SERVER = "127.0.0.1"
11 |
12 | # Zabbix server authentication credentials
13 | ZABBIX_AUTH = {
14 | "user": "Admin", # Zabbix user name for authentication
15 | "password": "zabbix" # Zabbix user password for authentication
16 | }
17 |
18 | # IDs of items for which the history should be cleared
19 | ITEM_IDS = [70060]
20 |
21 |
22 | async def main():
23 | """
24 | The main function to perform asynchronous tasks.
25 | """
26 |
27 | # Create an instance of the AsyncZabbixAPI class
28 | api = AsyncZabbixAPI(ZABBIX_SERVER)
29 |
30 | # Authenticating with Zabbix API using the provided username and password.
31 | await api.login(**ZABBIX_AUTH)
32 |
33 | # Clear history for items with specified IDs
34 | try:
35 | await api.history.clear(*ITEM_IDS)
36 |
37 | # Alternative way to do the same (since v2.0.2):
38 | # await api.history.clear(ITEM_IDS)
39 | except APIRequestError as e:
40 | print(f"An error occurred when attempting to delete items: {e}")
41 | else:
42 | # Logout to release the Zabbix API session
43 | await api.logout()
44 |
45 | # Run the main coroutine
46 | asyncio.run(main())
47 |
--------------------------------------------------------------------------------
/examples/api/asynchronous/custom_client_session.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | import asyncio
7 | from zabbix_utils import AsyncZabbixAPI
8 | from aiohttp import ClientSession, TCPConnector
9 |
10 |
11 | # Zabbix server URL or IP address
12 | ZABBIX_SERVER = "127.0.0.1"
13 |
14 | # Zabbix server authentication credentials
15 | ZABBIX_AUTH = {
16 | "user": "Admin", # Zabbix user name for authentication
17 | "password": "zabbix" # Zabbix user password for authentication
18 | }
19 |
20 |
21 | async def main():
22 | """
23 | The main function to perform asynchronous tasks.
24 | """
25 |
26 | # Create an asynchronous client session for HTTP requests
27 | client_session = ClientSession(
28 | connector=TCPConnector(ssl=False)
29 | )
30 |
31 | # Create an instance of the AsyncZabbixAPI class
32 | api = AsyncZabbixAPI(
33 | url=ZABBIX_SERVER,
34 | client_session=client_session
35 | )
36 |
37 | # Authenticating with Zabbix API using the provided username and password.
38 | await api.login(**ZABBIX_AUTH)
39 |
40 | # Retrieve a list of hosts from the Zabbix server, including their host ID and name
41 | hosts = await api.host.get(
42 | output=['hostid', 'name']
43 | )
44 |
45 | # Print the names of the retrieved hosts
46 | for host in hosts:
47 | print(host['name'])
48 |
49 | # Logout to release the Zabbix API session
50 | await api.logout()
51 |
52 | # Close asynchronous client session
53 | await client_session.close()
54 |
55 | # Run the main coroutine
56 | asyncio.run(main())
57 |
--------------------------------------------------------------------------------
/examples/api/asynchronous/custom_ssl_context.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | import ssl
7 | import asyncio
8 | from zabbix_utils import AsyncZabbixAPI
9 | from aiohttp import ClientSession, TCPConnector
10 |
11 |
12 | # Zabbix server URL or IP address
13 | ZABBIX_SERVER = "https://example.com"
14 |
15 | # Zabbix server authentication credentials
16 | ZABBIX_AUTH = {
17 | "user": "Admin", # Zabbix user name for authentication
18 | "password": "zabbix" # Zabbix user password for authentication
19 | }
20 |
21 |
22 | async def main():
23 | """
24 | The main function to perform asynchronous tasks.
25 | """
26 |
27 | # Create a default SSL context for secure connections
28 | # Load a custom certificate from the specified file path to verify the server
29 | ctx = ssl.create_default_context()
30 | ctx.load_verify_locations("/path/to/certificate.crt")
31 |
32 | # Create an asynchronous client session for HTTP requests
33 | client_session = ClientSession(
34 | connector=TCPConnector(ssl=ctx)
35 | )
36 |
37 | # Create an instance of the AsyncZabbixAPI class
38 | api = AsyncZabbixAPI(
39 | url=ZABBIX_SERVER,
40 | client_session=client_session
41 | )
42 |
43 | # Authenticating with Zabbix API using the provided username and password.
44 | await api.login(**ZABBIX_AUTH)
45 |
46 | # Retrieve a list of hosts from the Zabbix server, including their host ID and name
47 | hosts = await api.host.get(
48 | output=['hostid', 'name']
49 | )
50 |
51 | # Print the names of the retrieved hosts
52 | for host in hosts:
53 | print(host['name'])
54 |
55 | # Logout to release the Zabbix API session
56 | await api.logout()
57 |
58 | # Close asynchronous client session
59 | await client_session.close()
60 |
61 | # Run the main coroutine
62 | asyncio.run(main())
63 |
--------------------------------------------------------------------------------
/examples/api/asynchronous/delete_items.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | import asyncio
7 | from zabbix_utils import AsyncZabbixAPI, APIRequestError
8 |
9 | # Zabbix server URL or IP address
10 | ZABBIX_SERVER = "127.0.0.1"
11 |
12 | # Zabbix server authentication credentials
13 | ZABBIX_AUTH = {
14 | "user": "Admin", # Zabbix user name for authentication
15 | "password": "zabbix" # Zabbix user password for authentication
16 | }
17 |
18 | # Item IDs to be deleted
19 | ITEM_IDS = [70060]
20 |
21 |
22 | async def main():
23 | """
24 | The main function to perform asynchronous tasks.
25 | """
26 |
27 | # Create an instance of the AsyncZabbixAPI class
28 | api = AsyncZabbixAPI(ZABBIX_SERVER)
29 |
30 | # Authenticating with Zabbix API using the provided username and password.
31 | await api.login(**ZABBIX_AUTH)
32 |
33 | # Delete items with specified IDs
34 | try:
35 | await api.item.delete(*ITEM_IDS)
36 |
37 | # Alternative way to do the same (since v2.0.2):
38 | # await api.item.delete(ITEM_IDS)
39 | except APIRequestError as e:
40 | print(f"An error occurred when attempting to delete items: {e}")
41 | else:
42 | # Logout to release the Zabbix API session
43 | await api.logout()
44 |
45 | # Run the main coroutine
46 | asyncio.run(main())
47 |
--------------------------------------------------------------------------------
/examples/api/asynchronous/disabling_validate_certs.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | import asyncio
7 | from zabbix_utils import AsyncZabbixAPI
8 |
9 |
10 | # SSL certificate verification will be ignored.
11 | # This can be useful in some cases, but it also poses security risks because
12 | # it makes the connection susceptible to man-in-the-middle attacks.
13 | ZABBIX_PARAMS = {
14 | "url": "127.0.0.1",
15 | "validate_certs": False
16 | }
17 |
18 | # Zabbix server authentication credentials.
19 | ZABBIX_AUTH = {
20 | "user": "Admin",
21 | "password": "zabbix"
22 | }
23 |
24 |
25 | async def main():
26 | """
27 | The main function to perform asynchronous tasks.
28 | """
29 |
30 | # Create an instance of the AsyncZabbixAPI class with the specified authentication details.
31 | # Note: Ignoring SSL certificate validation may expose the connection to security risks.
32 | api = AsyncZabbixAPI(**ZABBIX_PARAMS)
33 |
34 | # Authenticating with Zabbix API using the provided username and password.
35 | await api.login(**ZABBIX_AUTH)
36 |
37 | # Retrieve a list of users from the Zabbix server, including their user ID and name.
38 | users = await api.user.get(
39 | output=['userid', 'name']
40 | )
41 |
42 | # Print the names of the retrieved users.
43 | for user in users:
44 | print(user['name'])
45 |
46 | # Logout to release the Zabbix API session.
47 | await api.logout()
48 |
49 | # Run the main coroutine
50 | asyncio.run(main())
51 |
--------------------------------------------------------------------------------
/examples/api/asynchronous/export_templates.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | import asyncio
7 | from zabbix_utils import AsyncZabbixAPI
8 |
9 |
10 | # Zabbix server URL or IP address
11 | ZABBIX_SERVER = "127.0.0.1"
12 |
13 | # Zabbix server authentication credentials
14 | ZABBIX_AUTH = {
15 | "user": "Admin", # Zabbix user name for authentication
16 | "password": "zabbix" # Zabbix user password for authentication
17 | }
18 |
19 | # Template IDs to be exported
20 | TEMPLATE_IDS = [10050]
21 |
22 | # File path and format for exporting configuration
23 | FILE_PATH = "templates_export_example.{}"
24 |
25 |
26 | async def main():
27 | """
28 | The main function to perform asynchronous tasks.
29 | """
30 |
31 | # Create an instance of the ZabbixAPI class with the specified authentication details
32 | api = AsyncZabbixAPI(ZABBIX_SERVER)
33 |
34 | # Authenticating with Zabbix API using the provided token.
35 | await api.login(**ZABBIX_AUTH)
36 |
37 | # Determine the export file format based on the Zabbix API version
38 | export_format = "yaml"
39 | if api.version < 5.4:
40 | export_format = "xml"
41 |
42 | # Export configuration for specified template IDs
43 | configuration = await api.configuration.export(
44 | options={
45 | "templates": TEMPLATE_IDS
46 | },
47 | format=export_format
48 | )
49 |
50 | # Write the exported configuration to a file
51 | with open(FILE_PATH.format(export_format), mode='w', encoding='utf-8') as f:
52 | f.write(configuration)
53 |
54 | # Logout to release the Zabbix API session
55 | await api.logout()
56 |
57 | # Run the main coroutine
58 | asyncio.run(main())
59 |
--------------------------------------------------------------------------------
/examples/api/asynchronous/get_request_parameters.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | import asyncio
7 | from zabbix_utils import AsyncZabbixAPI
8 |
9 | # Zabbix server URL or IP address
10 | ZABBIX_SERVER = "127.0.0.1"
11 |
12 | # Zabbix server authentication credentials
13 | ZABBIX_AUTH = {
14 | "user": "Admin", # Zabbix user name for authentication
15 | "password": "zabbix" # Zabbix user password for authentication
16 | }
17 |
18 |
19 | async def main():
20 | """
21 | The main function to perform asynchronous tasks.
22 | """
23 |
24 | # Create an instance of the AsyncZabbixAPI class
25 | api = AsyncZabbixAPI(ZABBIX_SERVER)
26 |
27 | # Authenticating with Zabbix API using the provided username and password.
28 | await api.login(**ZABBIX_AUTH)
29 |
30 | # There are only three ways to pass parameters of type dictionary:
31 | #
32 | # 1. Specifying values directly with their keys:
33 | problems = await api.problem.get(tags=[{"tag": "scope", "value": "notice", "operator": "0"}])
34 | #
35 | # 2. Unpacking dictionary keys and values using `**`:
36 | # request_params = {"tags": [{"tag": "scope", "value": "notice", "operator": "0"}]}
37 | # problems = await api.problem.get(**request_params)
38 | #
39 | # 3. Passing the dictionary directly as an argument (since v2.0.2):
40 | # request_params = {"tags": [{"tag": "scope", "value": "notice", "operator": "0"}]}
41 | # problems = await api.problem.get(request_params)
42 |
43 | # Print the names of the retrieved users
44 | for problem in problems:
45 | print(problem['name'])
46 |
47 | # Logout to release the Zabbix API session
48 | await api.logout()
49 |
50 | # Run the main coroutine
51 | asyncio.run(main())
52 |
--------------------------------------------------------------------------------
/examples/api/asynchronous/use_context_manager.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | import asyncio
7 | from zabbix_utils import AsyncZabbixAPI
8 |
9 |
10 | # Zabbix server details and authentication credentials
11 | ZABBIX_SERVER = "127.0.0.1" # Zabbix server URL or IP address
12 | ZABBIX_USER = "Admin" # Zabbix user name for authentication
13 | ZABBIX_PASSWORD = "zabbix" # Zabbix user password for authentication
14 |
15 |
16 | async def main():
17 | """
18 | The main function to perform asynchronous tasks.
19 | """
20 |
21 | # Use a context manager for automatic logout and
22 | # close asynchronous session upon completion of the request.
23 | # Each time it's created it performs synchronously "apiinfo.version".
24 | # Highly recommended not to use it many times in a single script.
25 | async with AsyncZabbixAPI(url=ZABBIX_SERVER) as api:
26 | # Authenticate with the Zabbix API using the provided user credentials
27 | await api.login(user=ZABBIX_USER, password=ZABBIX_PASSWORD)
28 |
29 | # Retrieve a list of hosts from the Zabbix server, including their host ID and name
30 | hosts = await api.host.get(
31 | output=['hostid', 'name']
32 | )
33 |
34 | # Print the names of the retrieved hosts
35 | for host in hosts:
36 | print(host['name'])
37 |
38 | # Automatic logout occurs when the code block exits due to the context manager.
39 |
40 | # Run the main coroutine
41 | asyncio.run(main())
42 |
--------------------------------------------------------------------------------
/examples/api/asynchronous/using_http_auth.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | import asyncio
7 | from zabbix_utils import AsyncZabbixAPI
8 |
9 |
10 | async def main():
11 | """
12 | The main function to perform asynchronous tasks.
13 | """
14 |
15 | # Create an instance of the AsyncZabbixAPI class with the Zabbix server URL
16 | # Set Basic Authentication credentials for Zabbix API requests
17 | # Basic Authentication - a simple authentication mechanism used in HTTP.
18 | # It involves sending a username and password with each HTTP request.
19 | api = AsyncZabbixAPI(
20 | url="http://127.0.0.1",
21 | http_user="user",
22 | http_password="p@$sw0rd"
23 | )
24 |
25 | # Login to the Zabbix API using provided user credentials
26 | await api.login(user="Admin", password="zabbix")
27 |
28 | # Retrieve a list of users from the Zabbix server, including their user ID and name
29 | users = await api.user.get(
30 | output=['userid', 'name']
31 | )
32 |
33 | # Print the names of the retrieved users
34 | for user in users:
35 | print(user['name'])
36 |
37 | # Logout to release the Zabbix API session
38 | await api.logout()
39 |
40 | # Run the main coroutine
41 | asyncio.run(main())
42 |
--------------------------------------------------------------------------------
/examples/api/synchronous/auth_by_token.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | from zabbix_utils import ZabbixAPI
7 |
8 | # Use an authentication token generated via the web interface or
9 | # API instead of standard authentication by username and password.
10 | ZABBIX_AUTH = {
11 | "url": "127.0.0.1",
12 | "token": "8jF7sGh2Rp4TlQ1ZmXo0uYv3Bc6AiD9E"
13 | }
14 |
15 | # Create an instance of the ZabbixAPI class with the specified authentication details
16 | api = ZabbixAPI(**ZABBIX_AUTH)
17 |
18 | # Retrieve a list of users, including their user ID and name
19 | users = api.user.get(
20 | output=['userid', 'name']
21 | )
22 |
23 | # Print the names of the retrieved users
24 | for user in users:
25 | print(user['name'])
26 |
--------------------------------------------------------------------------------
/examples/api/synchronous/check_auth_state.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | from zabbix_utils import ZabbixAPI
7 |
8 | # Zabbix server details and authentication credentials
9 | ZABBIX_AUTH = {
10 | "url": "127.0.0.1", # Zabbix server URL or IP address
11 | "user": "Admin", # Zabbix user name for authentication
12 | "password": "zabbix" # Zabbix user password for authentication
13 | }
14 |
15 | # Create an instance of the ZabbixAPI class with the specified authentication details
16 | api = ZabbixAPI(**ZABBIX_AUTH)
17 |
18 | # Some actions when your session can be released
19 | # For example, api.logout()
20 |
21 | # Check if authentication is still valid
22 | if api.check_auth():
23 | # Retrieve a list of hosts from the Zabbix server, including their host ID and name
24 | hosts = api.host.get(
25 | output=['hostid', 'name']
26 | )
27 |
28 | # Print the names of the retrieved hosts
29 | for host in hosts:
30 | print(host['name'])
31 |
32 | # Logout to release the Zabbix API session
33 | api.logout()
34 |
--------------------------------------------------------------------------------
/examples/api/synchronous/clear_history.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | from zabbix_utils import ZabbixAPI, APIRequestError
7 |
8 | # Zabbix server details and authentication credentials
9 | ZABBIX_AUTH = {
10 | "url": "127.0.0.1", # Zabbix server URL or IP address
11 | "user": "Admin", # Zabbix user name for authentication
12 | "password": "zabbix" # Zabbix user password for authentication
13 | }
14 |
15 | # IDs of items for which the history should be cleared
16 | ITEM_IDS = [70060]
17 |
18 | # Create an instance of the ZabbixAPI class with the specified authentication details
19 | api = ZabbixAPI(**ZABBIX_AUTH)
20 |
21 | # Clear history for items with specified IDs
22 | try:
23 | api.history.clear(*ITEM_IDS)
24 |
25 | # Alternative way to do the same (since v2.0.2):
26 | # api.history.clear(*ITEM_IDS)
27 | except APIRequestError as e:
28 | print(f"An error occurred when attempting to clear items' history: {e}")
29 |
30 | # Logout to release the Zabbix API session
31 | api.logout()
32 |
--------------------------------------------------------------------------------
/examples/api/synchronous/custom_ssl_context.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | import ssl
7 | from zabbix_utils import ZabbixAPI
8 |
9 | # Create a default SSL context for secure connections
10 | # Load a custom certificate from the specified file path to verify the server
11 | ctx = ssl.create_default_context()
12 | ctx.load_verify_locations("/path/to/certificate.crt")
13 |
14 | # Create an instance of the ZabbixAPI class with the Zabbix server URL
15 | # Set ssl_context value for Zabbix API requests.
16 | ZABBIX_AUTH = {
17 | "url": "https://example.com",
18 | "user": "Admin",
19 | "password": "zabbix",
20 | "ssl_context": ctx
21 | }
22 |
23 | # Login to the Zabbix API using provided user credentials
24 | api = ZabbixAPI(**ZABBIX_AUTH)
25 |
26 | # Retrieve a list of hosts from the Zabbix server, including their host ID and name
27 | hosts = api.host.get(
28 | output=['hostid', 'name']
29 | )
30 |
31 | # Print the names of the retrieved hosts
32 | for host in hosts:
33 | print(host['name'])
34 |
35 | # Logout to release the Zabbix API session
36 | api.logout()
37 |
--------------------------------------------------------------------------------
/examples/api/synchronous/delete_items.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | from zabbix_utils import ZabbixAPI, APIRequestError
7 |
8 | # Zabbix server details and authentication credentials
9 | ZABBIX_AUTH = {
10 | "url": "127.0.0.1", # Zabbix server URL or IP address
11 | "user": "Admin", # Zabbix user name for authentication
12 | "password": "zabbix" # Zabbix user password for authentication
13 | }
14 |
15 | # Item IDs to be deleted
16 | ITEM_IDS = [70060]
17 |
18 | # Create an instance of the ZabbixAPI class with the specified authentication details
19 | api = ZabbixAPI(**ZABBIX_AUTH)
20 |
21 | # Delete items with specified IDs
22 | try:
23 | api.item.delete(*ITEM_IDS)
24 |
25 | # Alternative way to do the same (since v2.0.2):
26 | # api.item.delete(ITEM_IDS)
27 | except APIRequestError as e:
28 | print(f"An error occurred when attempting to delete items: {e}")
29 |
30 | # Logout to release the Zabbix API session
31 | api.logout()
32 |
--------------------------------------------------------------------------------
/examples/api/synchronous/disabling_validate_certs.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | from zabbix_utils import ZabbixAPI
7 |
8 | # SSL certificate verification will be ignored.
9 | # This can be useful in some cases, but it also poses security risks because
10 | # it makes the connection susceptible to man-in-the-middle attacks.
11 | ZABBIX_AUTH = {
12 | "url": "127.0.0.1",
13 | "user": "Admin",
14 | "password": "zabbix",
15 | "validate_certs": False
16 | }
17 |
18 | # Create an instance of the ZabbixAPI class with the specified authentication details
19 | # Note: Ignoring SSL certificate validation may expose the connection to security risks.
20 | api = ZabbixAPI(**ZABBIX_AUTH)
21 |
22 | # Retrieve a list of users from the Zabbix server, including their user ID and name
23 | users = api.user.get(
24 | output=['userid', 'name']
25 | )
26 |
27 | # Print the names of the retrieved users
28 | for user in users:
29 | print(user['name'])
30 |
31 | # Logout to release the Zabbix API session
32 | api.logout()
33 |
--------------------------------------------------------------------------------
/examples/api/synchronous/export_templates.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | from zabbix_utils import ZabbixAPI
7 |
8 | # Zabbix server details and authentication credentials
9 | ZABBIX_AUTH = {
10 | "url": "127.0.0.1", # Zabbix server URL or IP address
11 | "user": "Admin", # Zabbix user name for authentication
12 | "password": "zabbix" # Zabbix user password for authentication
13 | }
14 |
15 | # Template IDs to be exported
16 | TEMPLATE_IDS = [10050]
17 |
18 | # File path and format for exporting configuration
19 | FILE_PATH = "templates_export_example.{}"
20 |
21 | # Create an instance of the ZabbixAPI class with the specified authentication details
22 | api = ZabbixAPI(**ZABBIX_AUTH)
23 |
24 | # Determine the export file format based on the Zabbix API version
25 | export_format = "yaml"
26 | if api.version < 5.4:
27 | export_format = "xml"
28 |
29 | # Export configuration for specified template IDs
30 | configuration = api.configuration.export(
31 | options={
32 | "templates": TEMPLATE_IDS
33 | },
34 | format=export_format
35 | )
36 |
37 | # Write the exported configuration to a file
38 | with open(FILE_PATH.format(export_format), mode='w', encoding='utf-8') as f:
39 | f.write(configuration)
40 |
41 | # Logout to release the Zabbix API session
42 | api.logout()
43 |
--------------------------------------------------------------------------------
/examples/api/synchronous/get_request_parameters.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | from zabbix_utils import ZabbixAPI
7 |
8 | # Zabbix server details and authentication credentials
9 | ZABBIX_AUTH = {
10 | "url": "127.0.0.1", # Zabbix server URL or IP address
11 | "user": "Admin", # Zabbix user name for authentication
12 | "password": "zabbix" # Zabbix user password for authentication
13 | }
14 |
15 | # Create an instance of the ZabbixAPI class with the specified authentication details
16 | api = ZabbixAPI(**ZABBIX_AUTH)
17 |
18 | # There are only three ways to pass parameters of type dictionary:
19 | #
20 | # 1. Specifying values directly with their keys:
21 | problems = api.problem.get(tags=[{"tag": "scope", "value": "notice", "operator": "0"}])
22 | #
23 | # 2. Unpacking dictionary keys and values using `**`:
24 | # request_params = {"tags": [{"tag": "scope", "value": "notice", "operator": "0"}]}
25 | # problems = api.problem.get(**request_params)
26 | #
27 | # 3. Passing the dictionary directly as an argument (since v2.0.2):
28 | # request_params = {"tags": [{"tag": "scope", "value": "notice", "operator": "0"}]}
29 | # problems = api.problem.get(request_params)
30 |
31 | # Print the names of the retrieved users
32 | for problem in problems:
33 | print(problem['name'])
34 |
35 | # Logout to release the Zabbix API session
36 | api.logout()
37 |
--------------------------------------------------------------------------------
/examples/api/synchronous/token_auth_if_supported.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | from zabbix_utils import ZabbixAPI
7 |
8 | # Zabbix server details and authentication credentials
9 | ZABBIX_SERVER = "127.0.0.1" # Zabbix server URL or IP address
10 | ZABBIX_USER = "Admin" # Zabbix user name for authentication
11 | ZABBIX_PASSWORD = "zabbix" # Zabbix user password for authentication
12 | ZABBIX_TOKEN = "8jF7sGh2Rp4TlQ1ZmXo0uYv3Bc6AiD9E" # Authentication token for API access
13 |
14 | # Create an instance of the ZabbixAPI class with the specified Zabbix server URL
15 | api = ZabbixAPI(url=ZABBIX_SERVER)
16 |
17 | # Check Zabbix API version and authenticate accordingly
18 | # Zabbix API version can be compared with version expressed in float (major) or
19 | # string (full, i.e. "major.minor").
20 | if api.version >= 5.4:
21 | # If Zabbix API version is 5.4 or newer, use token-based authentication
22 | api.login(token=ZABBIX_TOKEN)
23 | else:
24 | # If Zabbix API version is older than 5.4, use traditional username and password authentication
25 | api.login(user=ZABBIX_USER, password=ZABBIX_PASSWORD)
26 |
27 | # Retrieve a list of users from the Zabbix server, including their user ID and name
28 | users = api.user.get(
29 | output=['userid', 'name']
30 | )
31 |
32 | # Print the names of the retrieved users
33 | for user in users:
34 | print(user['name'])
35 |
36 | # Logout to release the Zabbix API session
37 | api.logout()
38 |
--------------------------------------------------------------------------------
/examples/api/synchronous/use_context_manager.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | from zabbix_utils import ZabbixAPI
7 |
8 | # Zabbix server details and authentication credentials
9 | ZABBIX_SERVER = "127.0.0.1" # Zabbix server URL or IP address
10 | ZABBIX_USER = "Admin" # Zabbix user name for authentication
11 | ZABBIX_PASSWORD = "zabbix" # Zabbix user password for authentication
12 |
13 | # Use a context manager for automatic logout upon completion of the request.
14 | # Each time it's created it performs "login" and "apiinfo.version".
15 | # Highly recommended not to use it many times in a single script.
16 | with ZabbixAPI(url=ZABBIX_SERVER) as api:
17 | # Authenticate with the Zabbix API using the provided user credentials
18 | api.login(user=ZABBIX_USER, password=ZABBIX_PASSWORD)
19 |
20 | # Retrieve a list of hosts from the Zabbix server, including their host ID and name
21 | hosts = api.host.get(
22 | output=['hostid', 'name']
23 | )
24 |
25 | # Print the names of the retrieved hosts
26 | for host in hosts:
27 | print(host['name'])
28 |
29 | # Automatic logout occurs when the code block exits due to the context manager.
30 |
--------------------------------------------------------------------------------
/examples/api/synchronous/using_http_auth.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | from zabbix_utils import ZabbixAPI
7 |
8 | # Create an instance of the ZabbixAPI class with the Zabbix server URL
9 | # Set Basic Authentication credentials for Zabbix API requests
10 | # Basic Authentication - a simple authentication mechanism used in HTTP.
11 | # It involves sending a username and password with each HTTP request.
12 | api = ZabbixAPI(
13 | url="http://127.0.0.1",
14 | http_user="user",
15 | http_password="p@$sw0rd"
16 | )
17 |
18 | # Login to the Zabbix API using provided user credentials
19 | api.login(user="Admin", password="zabbix")
20 |
21 | # Retrieve a list of users from the Zabbix server, including their user ID and name
22 | users = api.user.get(
23 | output=['userid', 'name']
24 | )
25 |
26 | # Print the names of the retrieved users
27 | for user in users:
28 | print(user['name'])
29 |
30 | # Logout to release the Zabbix API session
31 | api.logout()
32 |
--------------------------------------------------------------------------------
/examples/get/asynchronous/custom_source_ip.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | import asyncio
7 | from zabbix_utils import AsyncGetter
8 |
9 |
10 | async def main():
11 | """
12 | The main function to perform asynchronous tasks.
13 | """
14 |
15 | # Create a AsyncGetter instance with specified parameters
16 | # Parameters: (host, port, source_ip)
17 | agent = AsyncGetter("127.0.0.1", 10050, source_ip="10.10.1.5")
18 |
19 | # Send a Zabbix agent query for system information (e.g., uname)
20 | resp = await agent.get('system.uname')
21 |
22 | # Check if there was an error in the response
23 | if resp.error:
24 | # Print the error message
25 | print("An error occurred while trying to get the value:", resp.error)
26 | else:
27 | # Print the value obtained for the specified item key item
28 | print("Received value:", resp.value)
29 |
30 | # Run the main coroutine
31 | asyncio.run(main())
32 |
--------------------------------------------------------------------------------
/examples/get/asynchronous/getting_value.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | import sys
7 | import json
8 | import asyncio
9 | from zabbix_utils import AsyncGetter
10 |
11 |
12 | async def main():
13 | """
14 | The main function to perform asynchronous tasks.
15 | """
16 |
17 | # Create a AsyncGetter instance for querying Zabbix agent
18 | agent = AsyncGetter(host='127.0.0.1', port=10050)
19 |
20 | # Send a Zabbix agent query for network interface discovery
21 | resp = await agent.get('net.if.discovery')
22 |
23 | # Check if there was an error in the response
24 | if resp.error:
25 | # Print the error message
26 | print("An error occurred while trying to get the value:", resp.error)
27 | # Exit the script
28 | sys.exit()
29 |
30 | try:
31 | # Attempt to parse the JSON response
32 | resp_list = json.loads(resp.value)
33 | except json.decoder.JSONDecodeError:
34 | print("Agent response decoding fails")
35 | # Exit the script if JSON decoding fails
36 | sys.exit()
37 |
38 | # Iterate through the discovered network interfaces and print their names
39 | for interface in resp_list:
40 | print(interface['{#IFNAME}'])
41 |
42 | # Run the main coroutine
43 | asyncio.run(main())
44 |
--------------------------------------------------------------------------------
/examples/get/synchronous/custom_source_ip.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | from zabbix_utils import Getter
7 |
8 | # Create a Getter instance with specified parameters
9 | # Parameters: (host, port, source_ip)
10 | agent = Getter("127.0.0.1", 10050, source_ip="10.10.1.5")
11 |
12 | # Send a Zabbix agent query for system information (e.g., uname)
13 | resp = agent.get('system.uname')
14 |
15 | # Check if there was an error in the response
16 | if resp.error:
17 | # Print the error message
18 | print("An error occurred while trying to get the value:", resp.error)
19 | else:
20 | # Print the value obtained for the specified item key item
21 | print("Received value:", resp.value)
22 |
--------------------------------------------------------------------------------
/examples/get/synchronous/getting_value.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | import sys
7 | import json
8 | from zabbix_utils import Getter
9 |
10 | # Create a Getter instance for querying Zabbix agent
11 | agent = Getter(host='127.0.0.1', port=10050)
12 |
13 | # Send a Zabbix agent query for network interface discovery
14 | resp = agent.get('net.if.discovery')
15 |
16 | # Check if there was an error in the response
17 | if resp.error:
18 | # Print the error message
19 | print("An error occurred while trying to get the value:", resp.error)
20 | # Exit the script
21 | sys.exit()
22 |
23 | try:
24 | # Attempt to parse the JSON response
25 | resp_list = json.loads(resp.value)
26 | except json.decoder.JSONDecodeError:
27 | print("Agent response decoding fails")
28 | # Exit the script if JSON decoding fails
29 | sys.exit()
30 |
31 | # Iterate through the discovered network interfaces and print their names
32 | for interface in resp_list:
33 | print(interface['{#IFNAME}'])
34 |
--------------------------------------------------------------------------------
/examples/get/synchronous/psk_wrapper.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | import ssl
7 | from zabbix_utils import Getter
8 |
9 | # Try importing sslpsk3, fall back to sslpsk2 if not available
10 | try:
11 | import sslpsk3 as sslpsk
12 | except ImportError:
13 | # Import sslpsk2 if sslpsk3 is not available
14 | import sslpsk2 as sslpsk
15 |
16 |
17 | # PSK wrapper function for SSL connection
18 | def psk_wrapper(sock):
19 | # Pre-Shared Key (PSK) and PSK Identity
20 | psk = bytes.fromhex('608b0a0049d41fdb35a824ef0a227f24e5099c60aa935e803370a961c937d6f7')
21 | psk_identity = b'PSKID'
22 |
23 | # Wrap the socket using sslpsk to establish an SSL connection with PSK
24 | return sslpsk.wrap_socket(
25 | sock,
26 | ssl_version=ssl.PROTOCOL_TLSv1_2,
27 | ciphers='ECDHE-PSK-AES128-CBC-SHA256',
28 | psk=(psk, psk_identity)
29 | )
30 |
31 |
32 | # Zabbix agent parameters
33 | ZABBIX_AGENT = "127.0.0.1"
34 | ZABBIX_PORT = 10050
35 |
36 | # Create a Getter instance with PSK support
37 | agent = Getter(
38 | host=ZABBIX_AGENT,
39 | port=ZABBIX_PORT,
40 | socket_wrapper=psk_wrapper
41 | )
42 |
43 | # Send a Zabbix agent query for system information (e.g., uname)
44 | resp = agent.get('system.uname')
45 |
46 | # Check if there was an error in the response
47 | if resp.error:
48 | # Print the error message
49 | print("An error occurred while trying to get the value:", resp.error)
50 | else:
51 | # Print the value obtained for the specified item key item
52 | print("Received value:", resp.value)
53 |
--------------------------------------------------------------------------------
/examples/sender/asynchronous/agent_clusters_using.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | import asyncio
7 | from zabbix_utils import ItemValue, AsyncSender
8 |
9 |
10 | # You can create an instance of AsyncSender specifying server address and port:
11 | #
12 | # sender = AsyncSender(server='127.0.0.1', port=10051)
13 | #
14 | # Or you can create an instance of AsyncSender specifying a list of Zabbix clusters:
15 | zabbix_clusters = [
16 | ['zabbix.cluster1.node1', 'zabbix.cluster1.node2:10051'],
17 | ['zabbix.cluster2.node1:10051', 'zabbix.cluster2.node2:20051', 'zabbix.cluster2.node3']
18 | ]
19 |
20 | # List of ItemValue instances representing items to be sent
21 | items = [
22 | ItemValue('host1', 'item.key1', 10),
23 | ItemValue('host1', 'item.key2', 'test message'),
24 | ItemValue('host2', 'item.key1', -1, 1695713666),
25 | ItemValue('host3', 'item.key1', '{"msg":"test message"}'),
26 | ItemValue('host2', 'item.key1', 0, 1695713666, 100)
27 | ]
28 |
29 |
30 | async def main():
31 | """
32 | The main function to perform asynchronous tasks.
33 | """
34 |
35 | sender = AsyncSender(clusters=zabbix_clusters)
36 | # You can also specify Zabbix clusters at the same time with server address and port:
37 | #
38 | # sender = AsyncSender(server='127.0.0.1', port=10051, clusters=zabbix_clusters)
39 | #
40 | # In such case, specified server address and port will be appended to the cluster list
41 | # as a cluster of a single node
42 |
43 | # Send multiple items to the Zabbix server/proxy and receive response
44 | response = await sender.send(items)
45 |
46 | # Check if the value sending was successful
47 | if response.failed == 0:
48 | # Print a success message along with the response time
49 | print(f"Value sent successfully in {response.time}")
50 | elif response.details:
51 | # Iterate through the list of responses from Zabbix server/proxy.
52 | for node, chunks in response.details.items():
53 | # Iterate through the list of chunks.
54 | for resp in chunks:
55 | # Check if the value sending was successful
56 | if resp.failed == 0:
57 | # Print a success message along with the response time
58 | print(f"Value sent successfully to {node} in {resp.time}")
59 | else:
60 | # Print a failure message
61 | print(f"Failed to send value to {node} at chunk step {resp.chunk}")
62 | else:
63 | # Print a failure message
64 | print("Failed to send value")
65 |
66 | # Run the main coroutine
67 | asyncio.run(main())
68 |
--------------------------------------------------------------------------------
/examples/sender/asynchronous/agent_config_using.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | import asyncio
7 | from zabbix_utils import AsyncSender
8 |
9 |
10 | async def main():
11 | """
12 | The main function to perform asynchronous tasks.
13 | """
14 |
15 | # You can create an instance of AsyncSender using the default configuration file path
16 | # (typically '/etc/zabbix/zabbix_agentd.conf')
17 | #
18 | # sender = AsyncSender(use_config=True)
19 | #
20 | # Or you can create an instance of AsyncSender using a custom configuration file path
21 | sender = AsyncSender(use_config=True, config_path='/etc/zabbix/zabbix_agent2.conf')
22 |
23 | # Send a value to a Zabbix server/proxy with specified parameters
24 | # Parameters: (host, key, value, clock)
25 | response = await sender.send_value('host', 'item.key', 'value', 1695713666)
26 |
27 | # Check if the value sending was successful
28 | if response.failed == 0:
29 | # Print a success message along with the response time
30 | print(f"Value sent successfully in {response.time}")
31 | elif response.details:
32 | # Iterate through the list of responses from Zabbix server/proxy.
33 | for node, chunks in response.details.items():
34 | # Iterate through the list of chunks.
35 | for resp in chunks:
36 | # Check if the value sending was successful
37 | if resp.failed == 0:
38 | # Print a success message along with the response time
39 | print(f"Value sent successfully to {node} in {resp.time}")
40 | else:
41 | # Print a failure message
42 | print(f"Failed to send value to {node} at chunk step {resp.chunk}")
43 | else:
44 | # Print a failure message
45 | print("Failed to send value")
46 |
47 | # Run the main coroutine
48 | asyncio.run(main())
49 |
--------------------------------------------------------------------------------
/examples/sender/asynchronous/bulk_sending.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | import asyncio
7 | from zabbix_utils import ItemValue, AsyncSender
8 |
9 |
10 | # List of ItemValue instances representing items to be sent
11 | items = [
12 | ItemValue('host1', 'item.key1', 10),
13 | ItemValue('host1', 'item.key2', 'test message'),
14 | ItemValue('host2', 'item.key1', -1, 1695713666),
15 | ItemValue('host3', 'item.key1', '{"msg":"test message"}'),
16 | ItemValue('host2', 'item.key1', 0, 1695713666, 100)
17 | ]
18 |
19 |
20 | async def main():
21 | """
22 | The main function to perform asynchronous tasks.
23 | """
24 |
25 | # Create an instance of the AsyncSender class with the specified server details
26 | sender = AsyncSender("127.0.0.1", 10051)
27 |
28 | # Send multiple items to the Zabbix server/proxy and receive response
29 | response = await sender.send(items)
30 |
31 | # Check if the value sending was successful
32 | if response.failed == 0:
33 | # Print a success message along with the response time
34 | print(f"Value sent successfully in {response.time}")
35 | elif response.details:
36 | # Iterate through the list of responses from Zabbix server/proxy.
37 | for node, chunks in response.details.items():
38 | # Iterate through the list of chunks.
39 | for resp in chunks:
40 | # Check if the value sending was successful
41 | if resp.failed == 0:
42 | # Print a success message along with the response time
43 | print(f"Value sent successfully to {node} in {resp.time}")
44 | else:
45 | # Print a failure message
46 | print(f"Failed to send value to {node} at chunk step {resp.chunk}")
47 | else:
48 | # Print a failure message
49 | print("Failed to send value")
50 |
51 | # Run the main coroutine
52 | asyncio.run(main())
53 |
--------------------------------------------------------------------------------
/examples/sender/asynchronous/custom_source_ip.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | import asyncio
7 | from zabbix_utils import AsyncSender
8 |
9 |
10 | async def main():
11 | """
12 | The main function to perform asynchronous tasks.
13 | """
14 |
15 | # Create an instance of the AsyncSender class with specified parameters
16 | # Parameters: (server, port, source_ip)
17 | sender = AsyncSender("127.0.0.1", 10051, source_ip="10.10.1.5")
18 |
19 | # Send a value to a Zabbix server/proxy with specified parameters
20 | # Parameters: (host, key, value, clock)
21 | response = await sender.send_value('host', 'item.key', 'value', 1695713666)
22 |
23 | # Check if the value sending was successful
24 | if response.failed == 0:
25 | # Print a success message along with the response time
26 | print(f"Value sent successfully in {response.time}")
27 | else:
28 | # Print a failure message
29 | print("Failed to send value")
30 |
31 | # Run the main coroutine
32 | asyncio.run(main())
33 |
--------------------------------------------------------------------------------
/examples/sender/asynchronous/single_sending.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | import asyncio
7 | from zabbix_utils import AsyncSender
8 |
9 |
10 | # Zabbix server/proxy details for AsyncSender
11 | ZABBIX_SERVER = {
12 | "server": "127.0.0.1", # Zabbix server/proxy IP address or hostname
13 | "port": 10051 # Zabbix server/proxy port for AsyncSender
14 | }
15 |
16 |
17 | async def main():
18 | """
19 | The main function to perform asynchronous tasks.
20 | """
21 |
22 | # Create an instance of the AsyncSender class with the specified server details
23 | sender = AsyncSender(**ZABBIX_SERVER)
24 |
25 | # Send a value to a Zabbix server/proxy with specified parameters
26 | # Parameters: (host, key, value, clock, ns)
27 | response = await sender.send_value('host', 'item.key', 'value', 1695713666, 30)
28 |
29 | # Check if the value sending was successful
30 | if response.failed == 0:
31 | # Print a success message along with the response time
32 | print(f"Value sent successfully in {response.time}")
33 | else:
34 | # Print a failure message
35 | print("Failed to send value")
36 |
37 | # Run the main coroutine
38 | asyncio.run(main())
39 |
--------------------------------------------------------------------------------
/examples/sender/asynchronous/tls_cert_context.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | import ssl
7 | import asyncio
8 | from zabbix_utils import AsyncSender
9 |
10 | # Zabbix server details
11 | ZABBIX_SERVER = "zabbix-server.example.com"
12 | ZABBIX_PORT = 10051
13 |
14 | # Paths to certificate and key files
15 | CA_PATH = 'path/to/cabundle.pem'
16 | CERT_PATH = 'path/to/agent.crt'
17 | KEY_PATH = 'path/to/agent.key'
18 |
19 |
20 | # Create and configure an SSL context for secure communication with the Zabbix server.
21 | def custom_context(*args, **kwargs) -> ssl.SSLContext:
22 |
23 | # Create an SSL context for TLS client
24 | context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
25 |
26 | # Load the client certificate and private key
27 | context.load_cert_chain(CERT_PATH, keyfile=KEY_PATH)
28 |
29 | # Load the certificate authority bundle file
30 | context.load_verify_locations(cafile=CA_PATH)
31 |
32 | # Disable hostname verification
33 | context.check_hostname = False
34 |
35 | # Set the verification mode to require a valid certificate
36 | context.verify_mode = ssl.VerifyMode.CERT_REQUIRED
37 |
38 | # Return created context
39 | return context
40 |
41 |
42 | async def main():
43 | """
44 | The main function to perform asynchronous tasks.
45 | """
46 |
47 | # Create an instance of AsyncSender with SSL context
48 | sender = AsyncSender(
49 | server=ZABBIX_SERVER,
50 | port=ZABBIX_PORT,
51 | ssl_context=custom_context
52 | )
53 |
54 | # Send a value to a Zabbix server/proxy with specified parameters
55 | # Parameters: (host, key, value, clock, ns)
56 | response = await sender.send_value('host', 'item.key', 'value', 1695713666, 30)
57 |
58 | # Check if the value sending was successful
59 | if response.failed == 0:
60 | # Print a success message along with the response time
61 | print(f"Value sent successfully in {response.time}")
62 | else:
63 | # Print a failure message
64 | print("Failed to send value")
65 |
66 | # Run the main coroutine
67 | asyncio.run(main())
68 |
--------------------------------------------------------------------------------
/examples/sender/asynchronous/tls_cert_context_from_config.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | import ssl
7 | import asyncio
8 | from zabbix_utils import AsyncSender
9 |
10 | # Zabbix server details
11 | ZABBIX_SERVER = "zabbix-server.example.com"
12 | ZABBIX_PORT = 10051
13 |
14 |
15 | # Create and configure an SSL context for secure communication with the Zabbix server.
16 | def custom_context(config) -> ssl.SSLContext:
17 |
18 | # Try to get paths to certificate and key files
19 | ca_path = config.get('tlscafile')
20 | cert_path = config.get('tlscertfile')
21 | key_path = config.get('tlskeyfile')
22 |
23 | # Create an SSL context for TLS client
24 | context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
25 |
26 | # Load the client certificate and private key
27 | context.load_cert_chain(cert_path, keyfile=key_path)
28 |
29 | # Load the certificate authority bundle file
30 | context.load_verify_locations(cafile=ca_path)
31 |
32 | # Disable hostname verification
33 | context.check_hostname = False
34 |
35 | # Set the verification mode to require a valid certificate
36 | context.verify_mode = ssl.VerifyMode.CERT_REQUIRED
37 |
38 | # Return created context
39 | return context
40 |
41 |
42 | async def main():
43 | """
44 | The main function to perform asynchronous tasks.
45 | """
46 |
47 | # Create an instance of AsyncSender with SSL context
48 | sender = AsyncSender(
49 | server=ZABBIX_SERVER,
50 | port=ZABBIX_PORT,
51 | ssl_context=custom_context
52 | )
53 |
54 | # Send a value to a Zabbix server/proxy with specified parameters
55 | # Parameters: (host, key, value, clock, ns)
56 | response = await sender.send_value('host', 'item.key', 'value', 1695713666, 30)
57 |
58 | # Check if the value sending was successful
59 | if response.failed == 0:
60 | # Print a success message along with the response time
61 | print(f"Value sent successfully in {response.time}")
62 | else:
63 | # Print a failure message
64 | print("Failed to send value")
65 |
66 | # Run the main coroutine
67 | asyncio.run(main())
68 |
--------------------------------------------------------------------------------
/examples/sender/synchronous/agent_clusters_using.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | from zabbix_utils import ItemValue, Sender
7 |
8 | # You can create an instance of Sender specifying server address and port:
9 | #
10 | # sender = Sender(server='127.0.0.1', port=10051)
11 | #
12 | # Or you can create an instance of Sender specifying a list of Zabbix clusters:
13 | zabbix_clusters = [
14 | ['zabbix.cluster1.node1', 'zabbix.cluster1.node2:10051'],
15 | ['zabbix.cluster2.node1:10051', 'zabbix.cluster2.node2:20051', 'zabbix.cluster2.node3']
16 | ]
17 | sender = Sender(clusters=zabbix_clusters)
18 | # You can also specify Zabbix clusters at the same time with server address and port:
19 | #
20 | # sender = Sender(server='127.0.0.1', port=10051, clusters=zabbix_clusters)
21 | #
22 | # In such case, specified server address and port will be appended to the cluster list
23 | # as a cluster of a single node
24 |
25 | # List of ItemValue instances representing items to be sent
26 | items = [
27 | ItemValue('host1', 'item.key1', 10),
28 | ItemValue('host1', 'item.key2', 'test message'),
29 | ItemValue('host2', 'item.key1', -1, 1695713666),
30 | ItemValue('host3', 'item.key1', '{"msg":"test message"}'),
31 | ItemValue('host2', 'item.key1', 0, 1695713666, 100)
32 | ]
33 |
34 | # Send multiple items to the Zabbix server/proxy and receive response
35 | response = sender.send(items)
36 |
37 | # Check if the value sending was successful
38 | if response.failed == 0:
39 | # Print a success message along with the response time
40 | print(f"Value sent successfully in {response.time}")
41 | elif response.details:
42 | # Iterate through the list of responses from Zabbix server/proxy.
43 | for node, chunks in response.details.items():
44 | # Iterate through the list of chunks.
45 | for resp in chunks:
46 | # Check if the value sending was successful
47 | if resp.failed == 0:
48 | # Print a success message along with the response time
49 | print(f"Value sent successfully to {node} in {resp.time}")
50 | else:
51 | # Print a failure message
52 | print(f"Failed to send value to {node} at chunk step {resp.chunk}")
53 | else:
54 | # Print a failure message
55 | print("Failed to send value")
56 |
--------------------------------------------------------------------------------
/examples/sender/synchronous/agent_config_using.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | from zabbix_utils import Sender
7 |
8 | # You can create an instance of Sender using the default configuration file path
9 | # (typically '/etc/zabbix/zabbix_agentd.conf')
10 | #
11 | # sender = Sender(use_config=True)
12 | #
13 | # Or you can create an instance of Sender using a custom configuration file path
14 | sender = Sender(use_config=True, config_path='/etc/zabbix/zabbix_agent2.conf')
15 |
16 | # Send a value to a Zabbix server/proxy with specified parameters
17 | # Parameters: (host, key, value, clock)
18 | response = sender.send_value('host', 'item.key', 'value', 1695713666)
19 |
20 | # Check if the value sending was successful
21 | if response.failed == 0:
22 | # Print a success message along with the response time
23 | print(f"Value sent successfully in {response.time}")
24 | elif response.details:
25 | # Iterate through the list of responses from Zabbix server/proxy.
26 | for node, chunks in response.details.items():
27 | # Iterate through the list of chunks.
28 | for resp in chunks:
29 | # Check if the value sending was successful
30 | if resp.failed == 0:
31 | # Print a success message along with the response time
32 | print(f"Value sent successfully to {node} in {resp.time}")
33 | else:
34 | # Print a failure message
35 | print(f"Failed to send value to {node} at chunk step {resp.chunk}")
36 | else:
37 | # Print a failure message
38 | print("Failed to send value")
39 |
--------------------------------------------------------------------------------
/examples/sender/synchronous/bulk_sending.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | from zabbix_utils import ItemValue, Sender
7 |
8 | # List of ItemValue instances representing items to be sent
9 | items = [
10 | ItemValue('host1', 'item.key1', 10),
11 | ItemValue('host1', 'item.key2', 'test message'),
12 | ItemValue('host2', 'item.key1', -1, 1695713666),
13 | ItemValue('host3', 'item.key1', '{"msg":"test message"}'),
14 | ItemValue('host2', 'item.key1', 0, 1695713666, 100)
15 | ]
16 |
17 | # Create an instance of the Sender class with the specified server details
18 | sender = Sender("127.0.0.1", 10051)
19 |
20 | # Send multiple items to the Zabbix server/proxy and receive response
21 | response = sender.send(items)
22 |
23 | # Check if the value sending was successful
24 | if response.failed == 0:
25 | # Print a success message along with the response time
26 | print(f"Value sent successfully in {response.time}")
27 | elif response.details:
28 | # Iterate through the list of responses from Zabbix server/proxy.
29 | for node, chunks in response.details.items():
30 | # Iterate through the list of chunks.
31 | for resp in chunks:
32 | # Check if the value sending was successful
33 | if resp.failed == 0:
34 | # Print a success message along with the response time
35 | print(f"Value sent successfully to {node} in {resp.time}")
36 | else:
37 | # Print a failure message
38 | print(f"Failed to send value to {node} at chunk step {resp.chunk}")
39 | else:
40 | # Print a failure message
41 | print("Failed to send value")
42 |
--------------------------------------------------------------------------------
/examples/sender/synchronous/custom_source_ip.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | from zabbix_utils import Sender
7 |
8 | # Create an instance of the Sender class with specified parameters
9 | # Parameters: (server, port, source_ip)
10 | sender = Sender("127.0.0.1", 10051, source_ip="10.10.1.5")
11 |
12 | # Send a value to a Zabbix server/proxy with specified parameters
13 | # Parameters: (host, key, value, clock)
14 | response = sender.send_value('host', 'item.key', 'value', 1695713666)
15 |
16 | # Check if the value sending was successful
17 | if response.failed == 0:
18 | # Print a success message along with the response time
19 | print(f"Value sent successfully in {response.time}")
20 | else:
21 | # Print a failure message
22 | print("Failed to send value")
23 |
--------------------------------------------------------------------------------
/examples/sender/synchronous/psk_wrapper.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | import ssl
7 | from zabbix_utils import Sender
8 |
9 | # Try importing sslpsk3, fall back to sslpsk2 if not available
10 | try:
11 | import sslpsk3 as sslpsk
12 | except ImportError:
13 | # Import sslpsk2 if sslpsk3 is not available
14 | import sslpsk2 as sslpsk
15 |
16 |
17 | # PSK wrapper function for SSL connection
18 | def psk_wrapper(sock, tls):
19 | # Pre-Shared Key (PSK) and PSK Identity
20 | psk = bytes.fromhex('608b0a0049d41fdb35a824ef0a227f24e5099c60aa935e803370a961c937d6f7')
21 | psk_identity = b'PSKID'
22 |
23 | return sslpsk.wrap_socket(
24 | sock,
25 | ssl_version=ssl.PROTOCOL_TLSv1_2,
26 | ciphers='ECDHE-PSK-AES128-CBC-SHA256',
27 | psk=(psk, psk_identity)
28 | )
29 |
30 |
31 | # Zabbix server details
32 | ZABBIX_SERVER = "127.0.0.1"
33 | ZABBIX_PORT = 10051
34 |
35 | # Create a Sender instance with PSK support
36 | sender = Sender(
37 | server=ZABBIX_SERVER,
38 | port=ZABBIX_PORT,
39 | socket_wrapper=psk_wrapper
40 | )
41 |
42 | # Send a value to a Zabbix server/proxy with specified parameters
43 | # Parameters: (host, key, value, clock, ns)
44 | response = sender.send_value('host', 'item.key', 'value', 1695713666, 30)
45 |
46 | # Check if the value sending was successful
47 | if response.failed == 0:
48 | # Print a success message along with the response time
49 | print(f"Value sent successfully in {response.time}")
50 | else:
51 | # Print a failure message
52 | print("Failed to send value")
53 |
--------------------------------------------------------------------------------
/examples/sender/synchronous/psk_wrapper_from_config.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | import ssl
7 | from zabbix_utils import Sender
8 |
9 | # Try importing sslpsk3, fall back to sslpsk2 if not available
10 | try:
11 | import sslpsk3 as sslpsk
12 | except ImportError:
13 | # Import sslpsk2 if sslpsk3 is not available
14 | import sslpsk2 as sslpsk
15 |
16 |
17 | # PSK wrapper function for SSL connection
18 | def psk_wrapper(sock, config):
19 | psk = None
20 | psk_identity = config.get('tlspskidentity').encode('utf-8')
21 | psk_file = config.get('tlspskfile')
22 |
23 | # Read PSK from file if specified
24 | if psk_file:
25 | with open(psk_file, encoding='utf-8') as f:
26 | psk = bytes.fromhex(f.read())
27 |
28 | # Check if both PSK and PSK identity are available
29 | if psk and psk_identity:
30 | return sslpsk.wrap_socket(
31 | sock,
32 | ssl_version=ssl.PROTOCOL_TLSv1_2,
33 | ciphers='ECDHE-PSK-AES128-CBC-SHA256',
34 | psk=(psk, psk_identity)
35 | )
36 |
37 | # Return original socket if PSK or PSK identity is missing
38 | return sock
39 |
40 |
41 | # Create a Sender instance with PSK support
42 | sender = Sender(use_config=True, socket_wrapper=psk_wrapper)
43 |
44 | # Send a value to a Zabbix server/proxy with specified parameters
45 | # Parameters: (host, key, value, clock, ns)
46 | response = sender.send_value('host', 'item.key', 'value', 1695713666, 30)
47 |
48 | # Check if the value sending was successful
49 | if response.failed == 0:
50 | # Print a success message along with the response time
51 | print(f"Value sent successfully in {response.time}")
52 | else:
53 | # Print a failure message
54 | print("Failed to send value")
55 |
--------------------------------------------------------------------------------
/examples/sender/synchronous/single_sending.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | from zabbix_utils import Sender
7 |
8 | # Zabbix server/proxy details for Sender
9 | ZABBIX_SERVER = {
10 | "server": "127.0.0.1", # Zabbix server/proxy IP address or hostname
11 | "port": 10051 # Zabbix server/proxy port for Sender
12 | }
13 |
14 | # Create an instance of the Sender class with the specified server details
15 | sender = Sender(**ZABBIX_SERVER)
16 |
17 | # Send a value to a Zabbix server/proxy with specified parameters
18 | # Parameters: (host, key, value, clock, ns)
19 | response = sender.send_value('host', 'item.key', 'value', 1695713666, 30)
20 |
21 | # Check if the value sending was successful
22 | if response.failed == 0:
23 | # Print a success message along with the response time
24 | print(f"Value sent successfully in {response.time}")
25 | else:
26 | # Print a failure message
27 | print("Failed to send value")
28 |
--------------------------------------------------------------------------------
/examples/sender/synchronous/tls_cert_wrapper.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | import ssl
7 | from zabbix_utils import Sender
8 |
9 | # Zabbix server details
10 | ZABBIX_SERVER = "zabbix-server.example.com"
11 | ZABBIX_PORT = 10051
12 |
13 | # Paths to certificate and key files
14 | CA_PATH = 'path/to/cabundle.pem'
15 | CERT_PATH = 'path/to/agent.crt'
16 | KEY_PATH = 'path/to/agent.key'
17 |
18 |
19 | # Define a function for wrapping the socket with TLS
20 | def tls_wrapper(sock, *args, **kwargs):
21 |
22 | # Create an SSL context for TLS client
23 | context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
24 |
25 | # Load the client certificate and private key
26 | context.load_cert_chain(CERT_PATH, keyfile=KEY_PATH)
27 |
28 | # Load the certificate authority bundle file
29 | context.load_verify_locations(cafile=CA_PATH)
30 |
31 | # Disable hostname verification
32 | context.check_hostname = False
33 |
34 | # Set the verification mode to require a valid certificate
35 | context.verify_mode = ssl.VerifyMode.CERT_REQUIRED
36 |
37 | # Wrap the socket with TLS using the created context
38 | return context.wrap_socket(sock, server_hostname=ZABBIX_SERVER)
39 |
40 |
41 | # Create an instance of Sender with TLS configuration
42 | sender = Sender(
43 | server=ZABBIX_SERVER,
44 | port=ZABBIX_PORT,
45 | # Use the defined tls_wrapper function for socket wrapping
46 | socket_wrapper=tls_wrapper
47 | )
48 |
49 | # Send a value to a Zabbix server/proxy with specified parameters
50 | # Parameters: (host, key, value, clock, ns)
51 | response = sender.send_value('host', 'item.key', 'value', 1695713666, 30)
52 |
53 | # Check if the value sending was successful
54 | if response.failed == 0:
55 | # Print a success message along with the response time
56 | print(f"Value sent successfully in {response.time}")
57 | else:
58 | # Print a failure message
59 | print("Failed to send value")
60 |
--------------------------------------------------------------------------------
/examples/sender/synchronous/tls_cert_wrapper_from_config.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2001-2023 Zabbix SIA
2 | #
3 | # Zabbix SIA licenses this file to you under the MIT License.
4 | # See the LICENSE file in the project root for more information.
5 |
6 | import ssl
7 | from zabbix_utils import Sender
8 |
9 | # Zabbix server details
10 | ZABBIX_SERVER = "zabbix-server.example.com"
11 | ZABBIX_PORT = 10051
12 |
13 |
14 | # Define a function for wrapping the socket with TLS
15 | def tls_wrapper(sock, config):
16 |
17 | # Try to get paths to certificate and key files
18 | ca_path = config.get('tlscafile')
19 | cert_path = config.get('tlscertfile')
20 | key_path = config.get('tlskeyfile')
21 |
22 | # Create an SSL context for TLS client
23 | context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
24 |
25 | # Load the client certificate and private key
26 | context.load_cert_chain(cert_path, keyfile=key_path)
27 |
28 | # Load the certificate authority bundle file
29 | context.load_verify_locations(cafile=ca_path)
30 |
31 | # Disable hostname verification
32 | context.check_hostname = False
33 |
34 | # Set the verification mode to require a valid certificate
35 | context.verify_mode = ssl.VerifyMode.CERT_REQUIRED
36 |
37 | # Wrap the socket with TLS using the created context
38 | return context.wrap_socket(sock, server_hostname=ZABBIX_SERVER)
39 |
40 |
41 | # Create an instance of Sender with TLS configuration
42 | sender = Sender(
43 | server=ZABBIX_SERVER,
44 | port=ZABBIX_PORT,
45 | # Use the defined tls_wrapper function for socket wrapping
46 | socket_wrapper=tls_wrapper
47 | )
48 |
49 | # Send a value to a Zabbix server/proxy with specified parameters
50 | # Parameters: (host, key, value, clock, ns)
51 | response = sender.send_value('host', 'item.key', 'value', 1695713666, 30)
52 |
53 | # Check if the value sending was successful
54 | if response.failed == 0:
55 | # Print a success message along with the response time
56 | print(f"Value sent successfully in {response.time}")
57 | else:
58 | # Print a failure message
59 | print("Failed to send value")
60 |
--------------------------------------------------------------------------------
/requirements-dev.txt:
--------------------------------------------------------------------------------
1 | flake8>=3.0.0
2 | coverage
3 | aiohttp[speedups]>=3,<4
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | aiohttp[speedups]>=3,<4
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [flake8]
2 | max-line-length = 99
3 | exclude = tests/*,setup.py
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | # zabbix_utils
2 | #
3 | # Copyright (C) 2001-2023 Zabbix SIA
4 | #
5 | # Permission is hereby granted, free of charge, to any person
6 | # obtaining a copy of this software and associated documentation
7 | # files (the "Software"), to deal in the Software without restriction,
8 | # including without limitation the rights to use, copy, modify,
9 | # merge, publish, distribute, sublicense, and/or sell copies
10 | # of the Software, and to permit persons to whom the Software
11 | # is furnished to do so, subject to the following conditions:
12 |
13 | # The above copyright notice and this permission notice shall be
14 | # included in all copies or substantial portions of the Software.
15 |
16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18 | # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 | # OTHER DEALINGS IN THE SOFTWARE.
24 |
25 | import re
26 | import setuptools
27 | from zabbix_utils.version import __version__
28 |
29 | with open("README.md", "r", encoding="utf-8") as fh:
30 | regexp = r'(?<=##)\s*Get started\n*(^\*.*\n){1,10}\n*##'
31 | long_description = re.sub(regexp, '', fh.read(), flags=re.M)
32 |
33 | setuptools.setup(
34 | name="zabbix_utils",
35 | version=__version__,
36 | author="Zabbix SIA",
37 | author_email="integrationteam@zabbix.com",
38 | maintainer="Aleksandr Iantsen",
39 | maintainer_email="aleksandr.iantsen@zabbix.com",
40 | description="A library with modules for working with Zabbix (Zabbix API, Zabbix sender, Zabbix get)",
41 | long_description=long_description,
42 | long_description_content_type="text/markdown",
43 | keywords="monitoring zabbix api sender get utils tools",
44 | url="https://github.com/zabbix/python-zabbix-utils",
45 | test_suite='tests',
46 | packages=["zabbix_utils"],
47 | tests_require=["unittest"],
48 | install_requires=[],
49 | extras_require={
50 | "async": ["aiohttp>=3,<4"],
51 | },
52 | python_requires='>=3.8',
53 | project_urls={
54 | 'Zabbix': 'https://www.zabbix.com/documentation/current',
55 | 'Source': 'https://github.com/zabbix/python-zabbix-utils',
56 | 'Changes': 'https://github.com/zabbix/python-zabbix-utils/blob/main/CHANGELOG.md',
57 | 'Bug Tracker': 'https://github.com/zabbix/python-zabbix-utils/issues'
58 | },
59 | classifiers=[
60 | "Development Status :: 5 - Production/Stable",
61 | "Programming Language :: Python",
62 | "Programming Language :: Python :: 3",
63 | "Programming Language :: Python :: 3.8",
64 | "Programming Language :: Python :: 3.9",
65 | "Programming Language :: Python :: 3.10",
66 | "Programming Language :: Python :: 3.11",
67 | "Programming Language :: Python :: 3.12",
68 | "License :: OSI Approved :: MIT License",
69 | "Operating System :: OS Independent",
70 | "Topic :: System :: Monitoring",
71 | "Topic :: System :: Networking :: Monitoring",
72 | "Topic :: System :: Systems Administration",
73 | "Intended Audience :: Developers",
74 | "Intended Audience :: System Administrators",
75 | "Intended Audience :: Information Technology"
76 | ]
77 | )
78 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zabbix/python-zabbix-utils/8d57619c96cafa56990e71c0fd65ba9115507c1d/tests/__init__.py
--------------------------------------------------------------------------------
/tests/common.py:
--------------------------------------------------------------------------------
1 | # zabbix_utils
2 | #
3 | # Copyright (C) 2001-2023 Zabbix SIA
4 | #
5 | # Permission is hereby granted, free of charge, to any person
6 | # obtaining a copy of this software and associated documentation
7 | # files (the "Software"), to deal in the Software without restriction,
8 | # including without limitation the rights to use, copy, modify,
9 | # merge, publish, distribute, sublicense, and/or sell copies
10 | # of the Software, and to permit persons to whom the Software
11 | # is furnished to do so, subject to the following conditions:
12 |
13 | # The above copyright notice and this permission notice shall be
14 | # included in all copies or substantial portions of the Software.
15 |
16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18 | # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 | # OTHER DEALINGS IN THE SOFTWARE.
24 |
25 | import ssl
26 | import json
27 |
28 | from zabbix_utils.types import ItemValue
29 | from zabbix_utils.version import __min_supported__, __max_supported__
30 |
31 |
32 | API_DEFAULTS = {
33 | 'user': 'Admin',
34 | 'password': 'zabbix',
35 | 'token': 'oTmtWu',
36 | 'session': 'cc364fb50199c5e305aa91785b7e49a0',
37 | 'max_version': "{}.0".format(__max_supported__ + .2),
38 | 'min_version': "{}.0".format(__min_supported__ - .2)
39 | }
40 |
41 |
42 | GETTER_DEFAULTS = {
43 | 'host': 'localhost',
44 | 'port': 10050,
45 | 'source_ip': '192.168.1.1'
46 | }
47 |
48 | SENDER_DEFAULTS = {
49 | 'server': 'localhost',
50 | 'port': 10051,
51 | 'source_ip': '192.168.1.1',
52 | 'clusters': [
53 | ['zabbix.cluster.node1','zabbix.cluster.node2:20051'],
54 | ['zabbix.cluster2.node1','zabbix.cluster2.node2'],
55 | ['zabbix.domain']
56 | ]
57 | }
58 |
59 | ZABBIX_CONFIG = [
60 | f"""[root]
61 | ServerActive=zabbix.cluster.node1;zabbix.cluster.node2:20051,zabbix.cluster2.node1;zabbix.cluster2.node2,zabbix.domain
62 | Server={SENDER_DEFAULTS['server']}
63 | SourceIP={SENDER_DEFAULTS['source_ip']}
64 | TLSConnect=unencrypted
65 | TLSAccept=unencrypted
66 | """,
67 | f"""[root]
68 | Server={SENDER_DEFAULTS['server']}
69 | SourceIP={SENDER_DEFAULTS['source_ip']}
70 | """,
71 | f"""[root]
72 | SourceIP={SENDER_DEFAULTS['source_ip']}
73 | """
74 | ]
75 |
76 |
77 | class MockBasicAuth():
78 | login = API_DEFAULTS['user']
79 | password = API_DEFAULTS['password']
80 |
81 | class MockSessionConn():
82 | def __init__(self):
83 | self._ssl = None
84 | self.closed = False
85 | def close(self):
86 | self.closed = True
87 |
88 | class MockSession():
89 | def __init__(self, exception=None):
90 | self._default_auth = None
91 | self._connector = MockSessionConn()
92 | self.EXC = exception
93 | def set_auth(self):
94 | self._default_auth = MockBasicAuth()
95 | def del_auth(self):
96 | self._default_auth = None
97 | def set_ssl(self, ssl):
98 | self._connector._ssl = ssl
99 | def del_ssl(self):
100 | self._connector._ssl = None
101 | def set_exception(self, exception):
102 | self.EXC = exception
103 | def del_exception(self):
104 | self.EXC = None
105 | async def close(self):
106 | pass
107 | async def post(self, *args, **kwargs):
108 | if self.EXC:
109 | raise self.EXC()
110 | return MockAPIResponse()
111 |
112 |
113 | class MockAPIResponse():
114 | def __init__(self, exception=None):
115 | self.EXC = exception
116 | def set_exception(self, exception):
117 | self.EXC = exception
118 | def del_exception(self):
119 | self.EXC = None
120 | def raise_for_status(self):
121 | pass
122 | async def json(self, *args, **kwargs):
123 | if self.EXC:
124 | raise self.EXC()
125 | return {
126 | "jsonrpc": "2.0",
127 | "result": "{}.0".format(__max_supported__),
128 | "id": "0"
129 | }
130 | def read(self, *args, **kwargs):
131 | if self.EXC:
132 | raise self.EXC()
133 | return json.dumps({
134 | "jsonrpc": "2.0",
135 | "result": "{}.0".format(__max_supported__),
136 | "id": "0"
137 | }).encode('utf-8')
138 |
139 |
140 | class MockConnector():
141 | def __init__(self, input_stream, exception=None):
142 | self.STREAM = input_stream
143 | self.EXC = exception
144 | def __raiser(self, *args, **kwargs):
145 | if self.EXC:
146 | raise self.EXC()
147 | def connect(self, *args, **kwargs):
148 | self.__raiser(*args, **kwargs)
149 | def recv(self, bufsize, *args, **kwargs):
150 | self.__raiser(*args, **kwargs)
151 | resp = self.STREAM[0:bufsize]
152 | self.STREAM = self.STREAM[bufsize:]
153 | return resp
154 | def sendall(self, *args, **kwargs):
155 | self.__raiser(*args, **kwargs)
156 |
157 |
158 | class MockReader():
159 | STREAM = ''
160 | EXC = None
161 | @classmethod
162 | def set_stream(cls, stream):
163 | cls.STREAM = stream
164 | @classmethod
165 | def set_exception(cls, exception):
166 | cls.EXC = exception
167 | @classmethod
168 | async def readexactly(cls, length=0):
169 | if cls.EXC:
170 | raise cls.EXC()
171 | resp = cls.STREAM[0:length]
172 | cls.STREAM = cls.STREAM[length:]
173 | return resp
174 | @classmethod
175 | def close(cls):
176 | cls.EXC = None
177 |
178 |
179 | class MockWriter():
180 | EXC = None
181 | @classmethod
182 | def set_exception(cls, exception):
183 | cls.EXC = exception
184 | @classmethod
185 | def write(cls, *args, **kwargs):
186 | if cls.EXC:
187 | raise cls.EXC()
188 | @classmethod
189 | async def drain(cls, *args, **kwargs):
190 | pass
191 | @classmethod
192 | def close(cls):
193 | cls.EXC = None
194 | @classmethod
195 | async def wait_closed(cls):
196 | cls.EXC = None
197 |
198 | class MockLogger():
199 | def debug(self, *args, **kwargs):
200 | pass
201 | def error(self, *args, **kwargs):
202 | pass
203 | def warning(self, *args, **kwargs):
204 | pass
205 |
206 | def mock_send_sync_request(self, method, *args, **kwargs):
207 | result = {}
208 | if method == 'apiinfo.version':
209 | result = f"{__max_supported__}.0"
210 | elif method == 'user.login':
211 | result = API_DEFAULTS['session']
212 | elif method == 'user.logout':
213 | result = True
214 | elif method == 'user.checkAuthentication':
215 | result = {'userid': 42}
216 | return {'jsonrpc': '2.0', 'result': result, 'id': 1}
217 |
218 | async def mock_send_async_request(self, method, *args, **kwargs):
219 | result = {}
220 | if method == 'user.login':
221 | result = API_DEFAULTS['session']
222 | elif method == 'user.logout':
223 | result = True
224 | elif method == 'user.checkAuthentication':
225 | result = {'userid': 42}
226 | return {'jsonrpc': '2.0', 'result': result, 'id': 1}
227 |
228 | def socket_wrapper(connection, *args, **kwargs):
229 | return connection
230 |
231 | def ssl_context(*args, **kwargs):
232 | return ssl.create_default_context()
233 |
234 | def response_gen(items):
235 | def items_check(items):
236 | for i, item in enumerate(items):
237 | if isinstance(item, ItemValue):
238 | items[i] = item.to_json()
239 | return items
240 | info = {
241 | 'processed': len([i for i in items_check(items) if json.loads(i['value'])]),
242 | 'failed': len([i for i in items_check(items) if not json.loads(i['value'])]),
243 | 'total': len(items),
244 | 'seconds spent': '0.000100'
245 | }
246 | result = {
247 | 'response': 'success',
248 | 'info': '; '.join([f"{k}: {v}" for k,v in info.items()])
249 | }
250 |
251 | return result
252 |
--------------------------------------------------------------------------------
/tests/test_zabbix_aiogetter.py:
--------------------------------------------------------------------------------
1 | # zabbix_utils
2 | #
3 | # Copyright (C) 2001-2023 Zabbix SIA
4 | #
5 | # Permission is hereby granted, free of charge, to any person
6 | # obtaining a copy of this software and associated documentation
7 | # files (the "Software"), to deal in the Software without restriction,
8 | # including without limitation the rights to use, copy, modify,
9 | # merge, publish, distribute, sublicense, and/or sell copies
10 | # of the Software, and to permit persons to whom the Software
11 | # is furnished to do so, subject to the following conditions:
12 |
13 | # The above copyright notice and this permission notice shall be
14 | # included in all copies or substantial portions of the Software.
15 |
16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18 | # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 | # OTHER DEALINGS IN THE SOFTWARE.
24 |
25 | import json
26 | import socket
27 | import asyncio
28 | import unittest
29 |
30 | from tests import common
31 | from zabbix_utils import AsyncGetter
32 | from zabbix_utils import ProcessingError
33 |
34 |
35 | DEFAULT_VALUES = common.GETTER_DEFAULTS
36 |
37 |
38 | class TestAsyncGetter(unittest.IsolatedAsyncioTestCase):
39 | """Test cases for AsyncGetter object"""
40 |
41 | def test_init(self):
42 | """Tests creating of AsyncGetter object"""
43 |
44 | test_cases = [
45 | {
46 | 'input': {'source_ip': DEFAULT_VALUES['source_ip'], 'timeout': 20},
47 | 'output': json.dumps({
48 | "host": "127.0.0.1", "port": DEFAULT_VALUES['port'], "timeout": 20, "source_ip": DEFAULT_VALUES['source_ip'], "ssl_context": None
49 | })
50 | },
51 | {
52 | 'input': {'host':DEFAULT_VALUES['host']},
53 | 'output': json.dumps({
54 | "host": DEFAULT_VALUES['host'], "port": DEFAULT_VALUES['port'], "timeout": 10, "source_ip": None, "ssl_context": None
55 | })
56 | },
57 | {
58 | 'input': {'host':DEFAULT_VALUES['host'], 'port': 10150},
59 | 'output': json.dumps({
60 | "host": DEFAULT_VALUES['host'], "port": 10150, "timeout": 10, "source_ip": None, "ssl_context": None
61 | })
62 | }
63 | ]
64 |
65 | for case in test_cases:
66 |
67 | agent = AsyncGetter(**case['input'])
68 |
69 | self.assertEqual(json.dumps(agent.__dict__), case['output'],
70 | f"unexpected output with input data: {case['input']}")
71 |
72 | with self.assertRaises(TypeError,
73 | msg="expected TypeError exception hasn't been raised"):
74 | agent = AsyncGetter(ssl_context='wrapper', **case['input'])
75 |
76 | async def test_get_response(self):
77 | """Tests __get_response method in different cases"""
78 |
79 | async def test_case(input_stream):
80 | getter = AsyncGetter()
81 | reader = common.MockReader()
82 | reader.set_stream(input_stream)
83 | return await getter._AsyncGetter__get_response(reader)
84 |
85 | test_cases = [
86 | {'input': b'ZBXD\x01\x04\x00\x00\x00\x04\x00\x00\x00test', 'output': 'test'},
87 | {
88 | 'input': b'ZBXD\x01\x14\x00\x00\x00\x00\x00\x00\x00test_creating_packet',
89 | 'output': 'test_creating_packet'
90 | },
91 | {
92 | 'input': b'ZBXD\x03\x1d\x00\x00\x00\x15\x00\x00\x00x\x9c+I-.\x89O\xce\xcf-(J-.\xce\xcc\xcf\x8bO\xcbIL\x07\x00a\xd1\x08\xcb',
93 | 'output': 'test_compression_flag'
94 | }
95 | ]
96 |
97 | for case in test_cases:
98 | self.assertEqual(await test_case(case['input']), case['output'],
99 | f"unexpected output with input data: {case['input']}")
100 |
101 | with self.assertRaises(ProcessingError,
102 | msg="expected ProcessingError exception hasn't been raised"):
103 | await test_case(b'test')
104 |
105 | with self.assertRaises(ProcessingError,
106 | msg="expected ProcessingError exception hasn't been raised"):
107 | await test_case(b'ZBXD\x04\x04\x00\x00\x00\x00\x00\x00\x00test')
108 |
109 | with self.assertRaises(ProcessingError,
110 | msg="expected ProcessingError exception hasn't been raised"):
111 | await test_case(b'ZBXD\x00\x04\x00\x00\x00\x00\x00\x00\x00test')
112 |
113 | async def test_get(self):
114 | """Tests get() method in different cases"""
115 |
116 | output = 'test_response'
117 | response = b'ZBXD\x01\r\x00\x00\x00\x00\x00\x00\x00' + output.encode('utf-8')
118 |
119 | test_cases = [
120 | {
121 | 'connection': {'input_stream': response},
122 | 'input': {},
123 | 'output': output,
124 | 'raised': False
125 | },
126 | {
127 | 'connection': {'input_stream': response},
128 | 'input': {'source_ip': DEFAULT_VALUES['source_ip']},
129 | 'output': output,
130 | 'raised': False
131 | },
132 | {
133 | 'connection': {'input_stream': response},
134 | 'input': {'ssl_context': common.ssl_context},
135 | 'output': output,
136 | 'raised': False
137 | },
138 | {
139 | 'connection': {'input_stream': response, 'exception': TypeError},
140 | 'input': {'ssl_context': lambda: ''},
141 | 'output': output,
142 | 'raised': True
143 | },
144 | {
145 | 'connection': {'input_stream': response, 'exception': ConnectionResetError},
146 | 'input': {},
147 | 'output': output,
148 | 'raised': True
149 | },
150 | {
151 | 'connection': {'input_stream': response, 'exception': socket.error},
152 | 'input': {},
153 | 'output': output,
154 | 'raised': True
155 | },
156 | {
157 | 'connection': {'input_stream': response, 'exception': socket.gaierror},
158 | 'input': {},
159 | 'output': output,
160 | 'raised': True
161 | },
162 | {
163 | 'connection': {'input_stream': response, 'exception': asyncio.TimeoutError},
164 | 'input': {},
165 | 'output': output,
166 | 'raised': True
167 | }
168 | ]
169 |
170 | for case in test_cases:
171 |
172 | async def mock_open_connection(*args, **kwargs):
173 | reader = common.MockReader()
174 | reader.set_stream(case['connection'].get('input_stream',''))
175 | writer = common.MockWriter()
176 | writer.set_exception(case['connection'].get('exception'))
177 | return reader, writer
178 |
179 | with unittest.mock.patch.multiple(
180 | asyncio,
181 | open_connection=mock_open_connection):
182 |
183 | try:
184 | getter = AsyncGetter(**case['input'])
185 | except case['connection'].get('exception', Exception):
186 | if not case['raised']:
187 | self.fail(f"raised unexpected Exception with input data: {case['input']}")
188 |
189 | try:
190 | resp = await getter.get('system.uname')
191 | except case['connection'].get('exception', Exception):
192 | if not case['raised']:
193 | self.fail(f"raised unexpected Exception with input data: {case['input']}")
194 | else:
195 | self.assertEqual(resp.value, case['output'],
196 | f"unexpected output with input data: {case['input']}")
197 |
198 |
199 | if __name__ == '__main__':
200 | unittest.main()
201 |
--------------------------------------------------------------------------------
/tests/test_zabbix_common.py:
--------------------------------------------------------------------------------
1 | # zabbix_utils
2 | #
3 | # Copyright (C) 2001-2023 Zabbix SIA
4 | #
5 | # Permission is hereby granted, free of charge, to any person
6 | # obtaining a copy of this software and associated documentation
7 | # files (the "Software"), to deal in the Software without restriction,
8 | # including without limitation the rights to use, copy, modify,
9 | # merge, publish, distribute, sublicense, and/or sell copies
10 | # of the Software, and to permit persons to whom the Software
11 | # is furnished to do so, subject to the following conditions:
12 |
13 | # The above copyright notice and this permission notice shall be
14 | # included in all copies or substantial portions of the Software.
15 |
16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18 | # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 | # OTHER DEALINGS IN THE SOFTWARE.
24 |
25 | import unittest
26 |
27 | from zabbix_utils.common import ModuleUtils, ZabbixProtocol
28 |
29 |
30 | class TestModuleUtils(unittest.TestCase):
31 | """Test cases for ModuleUtils class"""
32 |
33 | def test_check_url(self):
34 | """Tests check_url method in different cases"""
35 |
36 | filename = ModuleUtils.JSONRPC_FILE
37 |
38 | test_cases = [
39 | {'input': '127.0.0.1', 'output': f"http://127.0.0.1/{filename}"},
40 | {'input': 'https://localhost', 'output': f"https://localhost/{filename}"},
41 | {'input': 'localhost/zabbix', 'output': f"http://localhost/zabbix/{filename}"},
42 | {'input': 'localhost/', 'output': f"http://localhost/{filename}"},
43 | {'input': f"127.0.0.1/{filename}", 'output': f"http://127.0.0.1/{filename}"}
44 | ]
45 |
46 | for case in test_cases:
47 | result = ModuleUtils.check_url(case['input'])
48 | self.assertEqual(result, case['output'],
49 | f"unexpected output with input data: {case['input']}")
50 |
51 | def test_mask_secret(self):
52 | """Tests mask_secret method in different cases"""
53 |
54 | mask = ModuleUtils.HIDING_MASK
55 |
56 | test_cases = [
57 | {'input': {'string': 'lZSwaQ', 'show_len': 5}, 'output': mask},
58 | {'input': {'string': 'ZWvaGS5SzNGaR990f', 'show_len': 4}, 'output': f"ZWva{mask}990f"},
59 | {'input': {'string': 'KZneJzgRzdlWcUjJj', 'show_len': 10}, 'output': mask},
60 | {'input': {'string': 'g5imzEr7TPcBG47fa', 'show_len': 20}, 'output': mask},
61 | {'input': {'string': 'In8y4eGughjBNSqEGPcqzejToVUT3OA4q5', 'show_len':2}, 'output': f"In{mask}q5"},
62 | {'input': {'string': 'Z8pZom5EVbRZ0W5wz', 'show_len':0}, 'output': mask}
63 | ]
64 |
65 | for case in test_cases:
66 | result = ModuleUtils.mask_secret(**case['input'])
67 | self.assertEqual(result, case['output'],
68 | f"unexpected output with input data: {case['input']}")
69 |
70 | def test_hide_private(self):
71 | """Tests hide_private method in different cases"""
72 |
73 | mask = ModuleUtils.HIDING_MASK
74 |
75 | test_cases = [
76 | {
77 | 'input': [{"auth": "q2BTIw85kqmjtXl3","token": "jZAC51wHuWdwvQnx"}],
78 | 'output': {"auth": mask, "token": mask}
79 | },
80 | {
81 | 'input': [{"token": "jZAC51wHuWdwvQnxwbP2T55vh6R5R2uW"}],
82 | 'output': {"token": f"jZAC{mask}R2uW"}
83 | },
84 | {
85 | 'input': [{"auth": "q2BTIw85kqmjtXl3zCgSSR26gwCGVFMK"}],
86 | 'output': {"auth": f"q2BT{mask}VFMK"}
87 | },
88 | {
89 | 'input': [{"sessionid": "p1xqXSf2HhYWa2ml6R5R2uWwbP2T55vh"}],
90 | 'output': {"sessionid": f"p1xq{mask}55vh"}
91 | },
92 | {
93 | 'input': [{"password": "HlphkcKgQKvofQHP"}],
94 | 'output': {"password": mask}
95 | },
96 | {
97 | 'input': [{"result": "p1xqXSf2HhYWa2ml6R5R2uWwbP2T55vh"}],
98 | 'output': {"result": f"p1xq{mask}55vh"}
99 | },
100 | {
101 | 'input': [{"result": "6.0.0"}],
102 | 'output': {"result": "6.0.0"}
103 | },
104 | {
105 | 'input': [{"result": ["10"]}],
106 | 'output': {"result": ["10"]}
107 | },
108 | {
109 | 'input': [{"result": [{"token": "jZAC51wHuWdwvQnxwbP2T55vh6R5R2uW"}]}],
110 | 'output': {"result": [{"token": f"jZAC{mask}R2uW"}]}
111 | },
112 | {
113 | 'input': [{"result": [["10"],["15"]]}],
114 | 'output': {"result": [["10"],["15"]]}
115 | },
116 | {
117 | 'input': [{"result": [[{"token": "jZAC51wHuWdwvQnxwbP2T55vh6R5R2uW"}]]}],
118 | 'output': {"result": [[{"token": f"jZAC{mask}R2uW"}]]}
119 | },
120 | {
121 | 'input': [{"result": ["jZAC51wHuWdwvQnxwbP2T55vh6R5R2uW"]}],
122 | 'output': {"result": [f"jZAC{mask}R2uW"]}
123 | },
124 | {
125 | 'input': [{"result": {"passwords": ["HlphkcKgQKvofQHP"]}}],
126 | 'output': {"result": {"passwords": [mask]}}
127 | },
128 | {
129 | 'input': [{"result": {"passwords": ["HlphkcKgQKvofQHP"]}}, {}],
130 | 'output': {"result": {"passwords": ["HlphkcKgQKvofQHP"]}}
131 | },
132 | {
133 | 'input': [{"result": {"tokens": ["jZAC51wHuWdwvQnxwbP2T55vh6R5R2uW"]}}],
134 | 'output': {"result": {"tokens": [f"jZAC{mask}R2uW"]}}
135 | },
136 | {
137 | 'input': [{"result": ["jZAC51wHuWdwvQnxwbP2T55vh6R5R2uW"]}, {}],
138 | 'output': {"result": [f"jZAC51wHuWdwvQnxwbP2T55vh6R5R2uW"]}
139 | }
140 | ]
141 |
142 | for case in test_cases:
143 | result = ModuleUtils.hide_private(*case['input'])
144 | self.assertEqual(result, case['output'],
145 | f"unexpected output with input data: {case['input']}")
146 |
147 |
148 | class TestZabbixProtocol(unittest.TestCase):
149 | """Test cases for ZabbixProtocol object"""
150 |
151 | def test_create_packet(self):
152 | """Tests create_packet method in different cases"""
153 |
154 | class Logger():
155 | def debug(self, *args, **kwargs):
156 | pass
157 |
158 | test_cases = [
159 | {
160 | 'input': {'payload':'test', 'log':Logger()},
161 | 'output': b'ZBXD\x01\x04\x00\x00\x00\x00\x00\x00\x00test'
162 | },
163 | {
164 | 'input': {'payload':'test_creating_packet', 'log':Logger()},
165 | 'output': b'ZBXD\x01\x14\x00\x00\x00\x00\x00\x00\x00test_creating_packet'
166 | },
167 | {
168 | 'input': {'payload':'test_compression_flag', 'log':Logger()},
169 | 'output': b'ZBXD\x01\x15\x00\x00\x00\x00\x00\x00\x00test_compression_flag'
170 | },
171 | {
172 | 'input': {'payload':'glāžšķūņu rūķīši', 'log':Logger()},
173 | 'output': b'ZBXD\x01\x1a\x00\x00\x00\x00\x00\x00\x00gl\xc4\x81\xc5\xbe\xc5\xa1\xc4\xb7\xc5\xab\xc5\x86u r\xc5\xab\xc4\xb7\xc4\xab\xc5\xa1i'
174 | }
175 | ]
176 |
177 | for case in test_cases:
178 | resp = ZabbixProtocol.create_packet(**case['input'])
179 | self.assertEqual(resp, case['output'],
180 | f"unexpected output with input data: {case['input']}")
181 |
182 |
183 | if __name__ == '__main__':
184 | unittest.main()
--------------------------------------------------------------------------------
/tests/test_zabbix_getter.py:
--------------------------------------------------------------------------------
1 | # zabbix_utils
2 | #
3 | # Copyright (C) 2001-2023 Zabbix SIA
4 | #
5 | # Permission is hereby granted, free of charge, to any person
6 | # obtaining a copy of this software and associated documentation
7 | # files (the "Software"), to deal in the Software without restriction,
8 | # including without limitation the rights to use, copy, modify,
9 | # merge, publish, distribute, sublicense, and/or sell copies
10 | # of the Software, and to permit persons to whom the Software
11 | # is furnished to do so, subject to the following conditions:
12 |
13 | # The above copyright notice and this permission notice shall be
14 | # included in all copies or substantial portions of the Software.
15 |
16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18 | # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 | # OTHER DEALINGS IN THE SOFTWARE.
24 |
25 | import json
26 | import socket
27 | import unittest
28 |
29 | from tests import common
30 | from zabbix_utils import Getter
31 | from zabbix_utils import ProcessingError
32 |
33 |
34 | DEFAULT_VALUES = common.GETTER_DEFAULTS
35 |
36 |
37 | class TestGetter(unittest.TestCase):
38 | """Test cases for Getter object"""
39 |
40 | def test_init(self):
41 | """Tests creating of Getter object"""
42 |
43 | test_cases = [
44 | {
45 | 'input': {'source_ip': DEFAULT_VALUES['source_ip'], 'timeout': 20},
46 | 'output': json.dumps({
47 | "host": "127.0.0.1", "port": DEFAULT_VALUES['port'], "timeout": 20, "use_ipv6": False, "source_ip": DEFAULT_VALUES['source_ip'], "socket_wrapper": None
48 | })
49 | },
50 | {
51 | 'input': {'host':DEFAULT_VALUES['host']},
52 | 'output': json.dumps({
53 | "host": DEFAULT_VALUES['host'], "port": DEFAULT_VALUES['port'], "timeout": 10, "use_ipv6": False, "source_ip": None, "socket_wrapper": None
54 | })
55 | },
56 | {
57 | 'input': {'host':DEFAULT_VALUES['host'], 'port': 10150},
58 | 'output': json.dumps({
59 | "host": DEFAULT_VALUES['host'], "port": 10150, "timeout": 10, "use_ipv6": False, "source_ip": None, "socket_wrapper": None
60 | })
61 | }
62 | ]
63 |
64 | for case in test_cases:
65 |
66 | agent = Getter(**case['input'])
67 |
68 | self.assertEqual(json.dumps(agent.__dict__), case['output'],
69 | f"unexpected output with input data: {case['input']}")
70 |
71 | with self.assertRaises(TypeError,
72 | msg="expected TypeError exception hasn't been raised"):
73 | agent = Getter(socket_wrapper='wrapper', **case['input'])
74 |
75 | def test_get_response(self):
76 | """Tests __get_response method in different cases"""
77 |
78 | test_cases = [
79 | {'input': b'ZBXD\x01\x04\x00\x00\x00\x04\x00\x00\x00test', 'output': 'test'},
80 | {
81 | 'input': b'ZBXD\x01\x14\x00\x00\x00\x00\x00\x00\x00test_creating_packet',
82 | 'output': 'test_creating_packet'
83 | },
84 | {
85 | 'input': b'ZBXD\x03\x1d\x00\x00\x00\x15\x00\x00\x00x\x9c+I-.\x89O\xce\xcf-(J-.\xce\xcc\xcf\x8bO\xcbIL\x07\x00a\xd1\x08\xcb',
86 | 'output': 'test_compression_flag'
87 | }
88 | ]
89 |
90 | for case in test_cases:
91 |
92 | getter = Getter()
93 | conn = common.MockConnector(case['input'])
94 |
95 | self.assertEqual(getter._Getter__get_response(conn), case['output'],
96 | f"unexpected output with input data: {case['input']}")
97 |
98 | with self.assertRaises(ProcessingError,
99 | msg="expected ProcessingError exception hasn't been raised"):
100 | getter = Getter()
101 | conn = common.MockConnector(b'test')
102 | getter._Getter__get_response(conn)
103 |
104 | with self.assertRaises(ProcessingError,
105 | msg="expected ProcessingError exception hasn't been raised"):
106 | getter = Getter()
107 | conn = common.MockConnector(b'ZBXD\x04\x04\x00\x00\x00\x00\x00\x00\x00test')
108 | getter._Getter__get_response(conn)
109 |
110 | with self.assertRaises(ProcessingError,
111 | msg="expected ProcessingError exception hasn't been raised"):
112 | getter = Getter()
113 | conn = common.MockConnector(b'ZBXD\x00\x04\x00\x00\x00\x00\x00\x00\x00test')
114 | getter._Getter__get_response(conn)
115 |
116 | def test_get(self):
117 | """Tests get() method in different cases"""
118 |
119 | output = 'test_response'
120 | response = b'ZBXD\x01\r\x00\x00\x00\x00\x00\x00\x00' + output.encode('utf-8')
121 |
122 | test_cases = [
123 | {
124 | 'connection': {'input_stream': response},
125 | 'input': {'use_ipv6': False},
126 | 'output': output,
127 | 'raised': False
128 | },
129 | {
130 | 'connection': {'input_stream': response},
131 | 'input': {'use_ipv6': True},
132 | 'output': output,
133 | 'raised': False
134 | },
135 | {
136 | 'connection': {'input_stream': response},
137 | 'input': {'source_ip': '127.0.0.1'},
138 | 'output': output,
139 | 'raised': False
140 | },
141 | {
142 | 'connection': {'input_stream': response},
143 | 'input': {'socket_wrapper': common.socket_wrapper},
144 | 'output': output,
145 | 'raised': False
146 | },
147 | {
148 | 'connection': {'input_stream': response, 'exception': socket.error},
149 | 'input': {},
150 | 'output': output,
151 | 'raised': True
152 | },
153 | {
154 | 'connection': {'input_stream': response, 'exception': socket.gaierror},
155 | 'input': {},
156 | 'output': output,
157 | 'raised': True
158 | },
159 | {
160 | 'connection': {'input_stream': response, 'exception': socket.timeout},
161 | 'input': {},
162 | 'output': output,
163 | 'raised': True
164 | }
165 | ]
166 |
167 | for case in test_cases:
168 | with unittest.mock.patch('socket.socket') as mock_socket:
169 | test_connector = common.MockConnector(**case['connection'])
170 | mock_socket.return_value.recv = test_connector.recv
171 | mock_socket.return_value.sendall = test_connector.sendall
172 | getter = Getter(**case['input'])
173 |
174 | try:
175 | resp = getter.get('system.uname')
176 | except case['connection'].get('exception', Exception):
177 | if not case['raised']:
178 | self.fail(f"raised unexpected Exception with input data: {case['input']}")
179 | else:
180 | self.assertEqual(resp.value, case['output'],
181 | f"unexpected output with input data: {case['input']}")
182 |
183 |
184 | if __name__ == '__main__':
185 | unittest.main()
186 |
--------------------------------------------------------------------------------
/zabbix_utils/__init__.py:
--------------------------------------------------------------------------------
1 | # zabbix_utils
2 | #
3 | # Copyright (C) 2001-2023 Zabbix SIA
4 | #
5 | # Permission is hereby granted, free of charge, to any person
6 | # obtaining a copy of this software and associated documentation
7 | # files (the "Software"), to deal in the Software without restriction,
8 | # including without limitation the rights to use, copy, modify,
9 | # merge, publish, distribute, sublicense, and/or sell copies
10 | # of the Software, and to permit persons to whom the Software
11 | # is furnished to do so, subject to the following conditions:
12 |
13 | # The above copyright notice and this permission notice shall be
14 | # included in all copies or substantial portions of the Software.
15 |
16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18 | # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 | # OTHER DEALINGS IN THE SOFTWARE.
24 |
25 | from .api import ZabbixAPI
26 | from .sender import Sender
27 | from .getter import Getter
28 | from .types import ItemValue, APIVersion
29 | from .exceptions import ModuleBaseException, APIRequestError, APINotSupported, ProcessingError
30 |
31 | from .aiosender import AsyncSender
32 | from .aiogetter import AsyncGetter
33 | try:
34 | __import__('aiohttp')
35 | except ModuleNotFoundError:
36 | class AsyncZabbixAPI():
37 | def __init__(self, *args, **kwargs):
38 | raise ModuleNotFoundError("No module named 'aiohttp'")
39 | else:
40 | from .aioapi import AsyncZabbixAPI
41 |
42 | __all__ = (
43 | 'ZabbixAPI',
44 | 'AsyncZabbixAPI',
45 | 'APIVersion',
46 | 'Sender',
47 | 'AsyncSender',
48 | 'ItemValue',
49 | 'Getter',
50 | 'AsyncGetter',
51 | 'ModuleBaseException',
52 | 'APIRequestError',
53 | 'APINotSupported',
54 | 'ProcessingError'
55 | )
56 |
--------------------------------------------------------------------------------
/zabbix_utils/aiogetter.py:
--------------------------------------------------------------------------------
1 | # zabbix_utils
2 | #
3 | # Copyright (C) 2001-2023 Zabbix SIA
4 | #
5 | # Permission is hereby granted, free of charge, to any person
6 | # obtaining a copy of this software and associated documentation
7 | # files (the "Software"), to deal in the Software without restriction,
8 | # including without limitation the rights to use, copy, modify,
9 | # merge, publish, distribute, sublicense, and/or sell copies
10 | # of the Software, and to permit persons to whom the Software
11 | # is furnished to do so, subject to the following conditions:
12 |
13 | # The above copyright notice and this permission notice shall be
14 | # included in all copies or substantial portions of the Software.
15 |
16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18 | # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 | # OTHER DEALINGS IN THE SOFTWARE.
24 |
25 | import ssl
26 | import socket
27 | import asyncio
28 | import logging
29 | from typing import Callable, Optional
30 |
31 | from .logger import EmptyHandler
32 | from .types import AgentResponse
33 | from .common import ZabbixProtocol
34 | from .exceptions import ProcessingError
35 |
36 | log = logging.getLogger(__name__)
37 | log.addHandler(EmptyHandler())
38 |
39 |
40 | class AsyncGetter():
41 | """Zabbix get asynchronous implementation.
42 |
43 | Args:
44 | host (str, optional): Zabbix agent address. Defaults to `'127.0.0.1'`.
45 |
46 | port (int, optional): Zabbix agent port. Defaults to `10050`.
47 |
48 | timeout (int, optional): Connection timeout value. Defaults to `10`.
49 |
50 | source_ip (str, optional): IP from which to establish connection. Defaults to `None`.
51 |
52 | ssl_context (Callable, optional): Func(), returned prepared ssl.SSLContext. \
53 | Defaults to `None`.
54 | """
55 |
56 | def __init__(self, host: str = '127.0.0.1', port: int = 10050, timeout: int = 10,
57 | source_ip: Optional[str] = None, ssl_context: Optional[Callable] = None):
58 | self.host = host
59 | self.port = port
60 | self.timeout = timeout
61 | self.source_ip = source_ip
62 |
63 | self.ssl_context = ssl_context
64 | if self.ssl_context:
65 | if not isinstance(self.ssl_context, Callable):
66 | raise TypeError('Value "ssl_context" should be a function.')
67 |
68 | async def __get_response(self, reader: asyncio.StreamReader) -> Optional[str]:
69 | result = await ZabbixProtocol.parse_async_packet(reader, log, ProcessingError)
70 |
71 | log.debug('Received data: %s', result)
72 |
73 | return result
74 |
75 | async def get(self, key: str) -> Optional[str]:
76 | """Gets item value from Zabbix agent by specified key.
77 |
78 | Args:
79 | key (str): Zabbix item key.
80 |
81 | Returns:
82 | str: Value from Zabbix agent for specified key.
83 | """
84 |
85 | packet = ZabbixProtocol.create_packet(key, log)
86 |
87 | connection_params = {
88 | "host": self.host,
89 | "port": self.port
90 | }
91 |
92 | if self.source_ip:
93 | connection_params['local_addr'] = (self.source_ip, 0)
94 |
95 | if self.ssl_context:
96 | connection_params['ssl'] = self.ssl_context()
97 | if not isinstance(connection_params['ssl'], ssl.SSLContext):
98 | raise TypeError(
99 | 'Function "ssl_context" must return "ssl.SSLContext".') from None
100 |
101 | connection = asyncio.open_connection(**connection_params)
102 |
103 | try:
104 | reader, writer = await asyncio.wait_for(connection, timeout=self.timeout)
105 | writer.write(packet)
106 | await writer.drain()
107 | except asyncio.TimeoutError as err:
108 | log.error(
109 | 'The connection to %s timed out after %d seconds',
110 | f"{self.host}:{self.port}",
111 | self.timeout
112 | )
113 | raise err
114 | except (ConnectionRefusedError, socket.gaierror) as err:
115 | log.error(
116 | 'An error occurred while trying to connect to %s: %s',
117 | f"{self.host}:{self.port}",
118 | getattr(err, 'msg', str(err))
119 | )
120 | raise err
121 | except (OSError, socket.error) as err:
122 | log.warning(
123 | 'An error occurred while trying to send to %s: %s',
124 | f"{self.host}:{self.port}",
125 | getattr(err, 'msg', str(err))
126 | )
127 | raise err
128 |
129 | try:
130 | response = await self.__get_response(reader)
131 | except (ConnectionResetError, asyncio.exceptions.IncompleteReadError) as err:
132 | log.debug('Get value error: %s', err)
133 | log.warning('Check access restrictions in Zabbix agent configuration.')
134 | raise err
135 | log.debug('Response from [%s:%s]: %s', self.host, self.port, response)
136 |
137 | writer.close()
138 | await writer.wait_closed()
139 |
140 | return AgentResponse(response)
141 |
--------------------------------------------------------------------------------
/zabbix_utils/exceptions.py:
--------------------------------------------------------------------------------
1 | # zabbix_utils
2 | #
3 | # Copyright (C) 2001-2023 Zabbix SIA
4 | #
5 | # Permission is hereby granted, free of charge, to any person
6 | # obtaining a copy of this software and associated documentation
7 | # files (the "Software"), to deal in the Software without restriction,
8 | # including without limitation the rights to use, copy, modify,
9 | # merge, publish, distribute, sublicense, and/or sell copies
10 | # of the Software, and to permit persons to whom the Software
11 | # is furnished to do so, subject to the following conditions:
12 |
13 | # The above copyright notice and this permission notice shall be
14 | # included in all copies or substantial portions of the Software.
15 |
16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18 | # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 | # OTHER DEALINGS IN THE SOFTWARE.
24 |
25 | from typing import Union
26 |
27 | from .common import ModuleUtils
28 |
29 |
30 | class ModuleBaseException(Exception):
31 | pass
32 |
33 |
34 | class APIRequestError(ModuleBaseException):
35 | """Exception class when Zabbix API returns error by request.
36 |
37 | Args:
38 | api_error (str|dict): Raw error message from Zabbix API.
39 | """
40 | def __init__(self, api_error: Union[str, dict]):
41 | if isinstance(api_error, dict):
42 | api_error['body'] = ModuleUtils.hide_private(api_error['body'])
43 | super().__init__("{message} {data}".format(**api_error))
44 | for key, value in api_error.items():
45 | setattr(self, key, value)
46 | else:
47 | super().__init__(api_error)
48 |
49 |
50 | class APINotSupported(ModuleBaseException):
51 | """Exception class when object/action is not supported by Zabbix API.
52 |
53 | Args:
54 | message (str): Not supported object/action message.
55 |
56 | version (str): Current version of Zabbix API.
57 | """
58 |
59 | def __init__(self, message: str, version: str = None):
60 | if version:
61 | message = f"{message} is unsupported for Zabbix {version} version"
62 | super().__init__(message)
63 |
64 |
65 | class ProcessingError(ModuleBaseException):
66 | def __init__(self, *args):
67 | super().__init__(" ".join(map(str, args)))
68 | return
69 |
--------------------------------------------------------------------------------
/zabbix_utils/getter.py:
--------------------------------------------------------------------------------
1 | # zabbix_utils
2 | #
3 | # Copyright (C) 2001-2023 Zabbix SIA
4 | #
5 | # Permission is hereby granted, free of charge, to any person
6 | # obtaining a copy of this software and associated documentation
7 | # files (the "Software"), to deal in the Software without restriction,
8 | # including without limitation the rights to use, copy, modify,
9 | # merge, publish, distribute, sublicense, and/or sell copies
10 | # of the Software, and to permit persons to whom the Software
11 | # is furnished to do so, subject to the following conditions:
12 |
13 | # The above copyright notice and this permission notice shall be
14 | # included in all copies or substantial portions of the Software.
15 |
16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18 | # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 | # OTHER DEALINGS IN THE SOFTWARE.
24 |
25 | import socket
26 | import logging
27 | from typing import Callable, Union
28 |
29 | from .logger import EmptyHandler
30 | from .types import AgentResponse
31 | from .common import ZabbixProtocol
32 | from .exceptions import ProcessingError
33 |
34 | log = logging.getLogger(__name__)
35 | log.addHandler(EmptyHandler())
36 |
37 |
38 | class Getter():
39 | """Zabbix get synchronous implementation.
40 |
41 | Args:
42 | host (str, optional): Zabbix agent address. Defaults to `'127.0.0.1'`.
43 |
44 | port (int, optional): Zabbix agent port. Defaults to `10050`.
45 |
46 | timeout (int, optional): Connection timeout value. Defaults to `10`.
47 |
48 | use_ipv6 (bool, optional): Specifying IPv6 use instead of IPv4. Defaults to `False`.
49 |
50 | source_ip (str, optional): IP from which to establish connection. Defaults to `None`.
51 |
52 | socket_wrapper (Callable, optional): Func(`conn`) to wrap socket. Defaults to `None`.
53 | """
54 |
55 | def __init__(self, host: str = '127.0.0.1', port: int = 10050, timeout: int = 10,
56 | use_ipv6: bool = False, source_ip: Union[str, None] = None,
57 | socket_wrapper: Union[Callable, None] = None):
58 | self.host = host
59 | self.port = port
60 | self.timeout = timeout
61 | self.use_ipv6 = use_ipv6
62 | self.source_ip = source_ip
63 |
64 | self.socket_wrapper = socket_wrapper
65 | if self.socket_wrapper:
66 | if not isinstance(self.socket_wrapper, Callable):
67 | raise TypeError('Value "socket_wrapper" should be a function.')
68 |
69 | def __get_response(self, conn: socket) -> Union[str, None]:
70 | result = ZabbixProtocol.parse_sync_packet(conn, log, ProcessingError)
71 |
72 | log.debug('Received data: %s', result)
73 |
74 | return result
75 |
76 | def get(self, key: str) -> Union[str, None]:
77 | """Gets item value from Zabbix agent by specified key.
78 |
79 | Args:
80 | key (str): Zabbix item key.
81 |
82 | Returns:
83 | str: Value from Zabbix agent for specified key.
84 | """
85 |
86 | packet = ZabbixProtocol.create_packet(key, log)
87 |
88 | try:
89 | if self.use_ipv6:
90 | connection = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
91 | else:
92 | connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
93 | except socket.error:
94 | raise ProcessingError(
95 | f"Error creating socket for {self.host}:{self.port}") from None
96 |
97 | connection.settimeout(self.timeout)
98 |
99 | if self.source_ip:
100 | connection.bind((self.source_ip, 0,))
101 |
102 | try:
103 | connection.connect((self.host, self.port))
104 | if self.socket_wrapper is not None:
105 | connection = self.socket_wrapper(connection)
106 | connection.sendall(packet)
107 | except (TimeoutError, socket.timeout) as err:
108 | log.error(
109 | 'The connection to %s timed out after %d seconds',
110 | f"{self.host}:{self.port}",
111 | self.timeout
112 | )
113 | connection.close()
114 | raise err
115 | except (ConnectionRefusedError, socket.gaierror) as err:
116 | log.error(
117 | 'An error occurred while trying to connect to %s: %s',
118 | f"{self.host}:{self.port}",
119 | getattr(err, 'msg', str(err))
120 | )
121 | connection.close()
122 | raise err
123 | except (OSError, socket.error) as err:
124 | log.warning(
125 | 'An error occurred while trying to send to %s: %s',
126 | f"{self.host}:{self.port}",
127 | getattr(err, 'msg', str(err))
128 | )
129 | connection.close()
130 | raise err
131 |
132 | try:
133 | response = self.__get_response(connection)
134 | except ConnectionResetError as err:
135 | log.debug('Get value error: %s', err)
136 | log.warning('Check access restrictions in Zabbix agent configuration.')
137 | raise err
138 | log.debug('Response from [%s:%s]: %s', self.host, self.port, response)
139 |
140 | try:
141 | connection.close()
142 | except socket.error:
143 | pass
144 |
145 | return AgentResponse(response)
146 |
--------------------------------------------------------------------------------
/zabbix_utils/logger.py:
--------------------------------------------------------------------------------
1 | # zabbix_utils
2 | #
3 | # Copyright (C) 2001-2023 Zabbix SIA
4 | #
5 | # Permission is hereby granted, free of charge, to any person
6 | # obtaining a copy of this software and associated documentation
7 | # files (the "Software"), to deal in the Software without restriction,
8 | # including without limitation the rights to use, copy, modify,
9 | # merge, publish, distribute, sublicense, and/or sell copies
10 | # of the Software, and to permit persons to whom the Software
11 | # is furnished to do so, subject to the following conditions:
12 |
13 | # The above copyright notice and this permission notice shall be
14 | # included in all copies or substantial portions of the Software.
15 |
16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18 | # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 | # OTHER DEALINGS IN THE SOFTWARE.
24 |
25 | import json
26 | import logging
27 | from .common import ModuleUtils
28 |
29 |
30 | class EmptyHandler(logging.Handler):
31 | """Empty logging handler."""
32 |
33 | def emit(self, *args, **kwargs):
34 | pass
35 |
36 |
37 | class SensitiveFilter(logging.Filter):
38 | """Filter to hide sensitive Zabbix info (password, auth) in logs"""
39 |
40 | def __init__(self, *args, **kwargs):
41 | super().__init__(*args, **kwargs)
42 |
43 | def __hide_data(self, raw_data):
44 | return json.dumps(ModuleUtils.hide_private(raw_data), indent=4, separators=(',', ': '))
45 |
46 | def filter(self, record):
47 | if isinstance(record.args, tuple):
48 | record.args = tuple(self.__hide_data(arg)
49 | if isinstance(arg, dict) else arg for arg in record.args)
50 | if isinstance(record.args, dict):
51 | record.args = self.__hide_data(record.args)
52 |
53 | return 1
54 |
--------------------------------------------------------------------------------
/zabbix_utils/version.py:
--------------------------------------------------------------------------------
1 | # zabbix_utils
2 | #
3 | # Copyright (C) 2001-2023 Zabbix SIA
4 | #
5 | # Permission is hereby granted, free of charge, to any person
6 | # obtaining a copy of this software and associated documentation
7 | # files (the "Software"), to deal in the Software without restriction,
8 | # including without limitation the rights to use, copy, modify,
9 | # merge, publish, distribute, sublicense, and/or sell copies
10 | # of the Software, and to permit persons to whom the Software
11 | # is furnished to do so, subject to the following conditions:
12 |
13 | # The above copyright notice and this permission notice shall be
14 | # included in all copies or substantial portions of the Software.
15 |
16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18 | # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 | # OTHER DEALINGS IN THE SOFTWARE.
24 |
25 | __version__ = "2.0.2"
26 |
27 | __min_supported__ = 5.0
28 | __max_supported__ = 7.2
29 |
--------------------------------------------------------------------------------