ezKit 1.11.15__tar.gz → 1.11.17__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (28) hide show
  1. {ezkit-1.11.15/ezKit.egg-info → ezkit-1.11.17}/PKG-INFO +1 -1
  2. ezkit-1.11.17/ezKit/dockerhub.py +75 -0
  3. ezkit-1.11.17/ezKit/zabbix.py +912 -0
  4. {ezkit-1.11.15 → ezkit-1.11.17/ezKit.egg-info}/PKG-INFO +1 -1
  5. {ezkit-1.11.15 → ezkit-1.11.17}/ezKit.egg-info/SOURCES.txt +2 -0
  6. {ezkit-1.11.15 → ezkit-1.11.17}/setup.py +1 -1
  7. {ezkit-1.11.15 → ezkit-1.11.17}/LICENSE +0 -0
  8. {ezkit-1.11.15 → ezkit-1.11.17}/MANIFEST.in +0 -0
  9. {ezkit-1.11.15 → ezkit-1.11.17}/README.md +0 -0
  10. {ezkit-1.11.15 → ezkit-1.11.17}/ezKit/__init__.py +0 -0
  11. {ezkit-1.11.15 → ezkit-1.11.17}/ezKit/_file.py +0 -0
  12. {ezkit-1.11.15 → ezkit-1.11.17}/ezKit/bottle.py +0 -0
  13. {ezkit-1.11.15 → ezkit-1.11.17}/ezKit/bottle_extensions.py +0 -0
  14. {ezkit-1.11.15 → ezkit-1.11.17}/ezKit/cipher.py +0 -0
  15. {ezkit-1.11.15 → ezkit-1.11.17}/ezKit/database.py +0 -0
  16. {ezkit-1.11.15 → ezkit-1.11.17}/ezKit/http.py +0 -0
  17. {ezkit-1.11.15 → ezkit-1.11.17}/ezKit/markdown_to_html.template +0 -0
  18. {ezkit-1.11.15 → ezkit-1.11.17}/ezKit/mongo.py +0 -0
  19. {ezkit-1.11.15 → ezkit-1.11.17}/ezKit/qywx.py +0 -0
  20. {ezkit-1.11.15 → ezkit-1.11.17}/ezKit/redis.py +0 -0
  21. {ezkit-1.11.15 → ezkit-1.11.17}/ezKit/sendemail.py +0 -0
  22. {ezkit-1.11.15 → ezkit-1.11.17}/ezKit/token.py +0 -0
  23. {ezkit-1.11.15 → ezkit-1.11.17}/ezKit/utils.py +0 -0
  24. {ezkit-1.11.15 → ezkit-1.11.17}/ezKit/xftp.py +0 -0
  25. {ezkit-1.11.15 → ezkit-1.11.17}/ezKit.egg-info/dependency_links.txt +0 -0
  26. {ezkit-1.11.15 → ezkit-1.11.17}/ezKit.egg-info/requires.txt +0 -0
  27. {ezkit-1.11.15 → ezkit-1.11.17}/ezKit.egg-info/top_level.txt +0 -0
  28. {ezkit-1.11.15 → ezkit-1.11.17}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: ezKit
3
- Version: 1.11.15
3
+ Version: 1.11.17
4
4
  Summary: Easy Kit
5
5
  Author: septvean
6
6
  Author-email: septvean@gmail.com
@@ -0,0 +1,75 @@
1
+ """Docker Hub"""
2
+ import requests
3
+ from loguru import logger
4
+
5
+
6
+ def get_latest_tags(url: str, limit: int = 20, proxies: dict | None = None) -> list | None:
7
+
8
+ info: str = "获取最新标签"
9
+
10
+ try:
11
+
12
+ logger.info(f"{info} [begin]")
13
+
14
+ # url = "https://hub.docker.com/v2/repositories/library/postgres/tags"
15
+
16
+ # 配置代理
17
+ # proxies = {
18
+ # "http": "http://127.0.0.1:1087",
19
+ # "https": "http://127.0.0.1:1087",
20
+ # }
21
+
22
+ # 请求接口
23
+ response = requests.get(url, params={"page": 1, "page_size": limit}, proxies=proxies, timeout=10)
24
+
25
+ # 检查请求状态码
26
+ if response.status_code != 200:
27
+ logger.error(f"{info} [请求接口错误, 状态码: {response.status_code}]")
28
+ return None
29
+
30
+ # 转换格式
31
+ data: dict = response.json()
32
+
33
+ results = data.get("results")
34
+
35
+ if results is None:
36
+ logger.error(f"{info} [请求结果错误]")
37
+ return None
38
+
39
+ # 提取 tags
40
+ tags = [tag["name"] for tag in results]
41
+
42
+ logger.success(f"{info} [success]")
43
+ return tags
44
+
45
+ except Exception as e:
46
+ logger.error(f"{info} [error]")
47
+ logger.exception(e)
48
+ return None
49
+
50
+ # def get_all_tags():
51
+ # url = "https://hub.docker.com/v2/repositories/library/postgres/tags"
52
+ # tags = []
53
+ # page = 1
54
+
55
+ # while True:
56
+ # response = requests.get(url, params={"page": page, "page_size": 100}, timeout=10)
57
+ # if response.status_code != 200:
58
+ # print(f"请求失败,状态码: {response.status_code}")
59
+ # break
60
+
61
+ # data = response.json()
62
+ # results = data.get("results", [])
63
+
64
+ # if not results:
65
+ # break
66
+
67
+ # tags.extend(tag["name"] for tag in results)
68
+ # page += 1
69
+
70
+ # return tags
71
+
72
+
73
+ # 获取所有 Postgres tags 并输出
74
+ # postgres_tags = get_all_tags()
75
+ # print("\n".join(postgres_tags))
@@ -0,0 +1,912 @@
1
+ import sys
2
+ import time
3
+ from copy import deepcopy
4
+
5
+ import requests
6
+ from loguru import logger
7
+
8
+ from . import utils
9
+
10
+
11
+ class Zabbix():
12
+ """Zabbix"""
13
+
14
+ api: str | None = None
15
+ auth: str | None = None
16
+
17
+ # ----------------------------------------------------------------------------------------------
18
+
19
+ def __init__(self, api: str, username: str, password: str):
20
+ """Initiation"""
21
+ self.api = api
22
+ self.auth = self.login(username=username, password=password)
23
+
24
+ # ----------------------------------------------------------------------------------------------
25
+
26
+ def request(
27
+ self,
28
+ method: str,
29
+ params: dict | list,
30
+ debug: bool = False,
31
+ **kwargs
32
+ ) -> dict | None:
33
+ """Request Zabbix API"""
34
+
35
+ try:
36
+
37
+ # https://www.zabbix.com/documentation/current/en/manual/api#performing-requests
38
+ # The request must have the Content-Type header set to one of these values:
39
+ # application/json-rpc, application/json or application/jsonrequest.
40
+ headers = {"Content-Type": "application/json-rpc"}
41
+
42
+ # https://www.zabbix.com/documentation/6.0/en/manual/api#authentication
43
+ # jsonrpc - the version of the JSON-RPC protocol used by the API; the Zabbix API implements JSON-RPC version 2.0
44
+ # method - the API method being called
45
+ # params - parameters that will be passed to the API method
46
+ # id - an arbitrary identifier of the request (请求标识符, 这里使用UNIX时间戳作为唯一标示)
47
+ # auth - a user authentication token; since we don't have one yet, it's set to null
48
+ data: dict = {
49
+ "jsonrpc": "2.0",
50
+ "method": method,
51
+ "params": params,
52
+ "auth": None if method == "apiinfo.version" else self.auth,
53
+ "id": int(time.time())
54
+ }
55
+
56
+ if utils.isTrue(debug, bool):
57
+ logger.info(f"request data: {data}")
58
+
59
+ if self.api is None:
60
+ logger.error("api is None")
61
+ return None
62
+
63
+ # 请求API
64
+ response = requests.post(self.api, headers=headers, json=data, timeout=10, **kwargs)
65
+
66
+ if utils.isTrue(debug, bool):
67
+ logger.info(f"response: {response}")
68
+
69
+ if response.status_code != 200:
70
+ logger.error(f"response status code: {response.status_code}")
71
+ return None
72
+
73
+ # 返回结果
74
+ return response.json()
75
+
76
+ except Exception as e:
77
+ logger.exception(e)
78
+ return None
79
+
80
+ # ----------------------------------------------------------------------------------------------
81
+
82
+ def login(self, username: str, password: str) -> str | None:
83
+ """User Login"""
84
+
85
+ info: str = "Login"
86
+
87
+ try:
88
+
89
+ logger.info(f"{info} [started]")
90
+
91
+ data: dict | None = self.request(
92
+ method="user.login",
93
+ params={"username": username, "password": password}
94
+ )
95
+
96
+ if data is None:
97
+ return None
98
+
99
+ if data.get("result") is None:
100
+ logger.error(f"{info} [response result is None]")
101
+ return None
102
+
103
+ logger.success(f"{info} [succeeded]")
104
+ return data["result"]
105
+
106
+ except Exception as e:
107
+ logger.error(f"{info} [failed]")
108
+ logger.exception(e)
109
+ return None
110
+
111
+ # ----------------------------------------------------------------------------------------------
112
+
113
+ def logout(self) -> bool:
114
+ """User Logout"""
115
+
116
+ info: str = "Logout"
117
+
118
+ try:
119
+
120
+ logger.info(f"{info} [started]")
121
+
122
+ data = self.request(method="user.logout", params={})
123
+
124
+ if data is not None and data.get("result"):
125
+ logger.success(f'{info} [succeeded]')
126
+ return True
127
+
128
+ if data is not None and data.get("error"):
129
+ logger.error(f"{info} [error: {data.get('error',{}).get('data')}]")
130
+ return False
131
+
132
+ logger.error(f"{info} [failed]")
133
+ return False
134
+
135
+ except Exception as e:
136
+ logger.error(f"{info} [failed]")
137
+ logger.exception(e)
138
+ return False
139
+
140
+ # ----------------------------------------------------------------------------------------------
141
+
142
+ # def logout_and_exit(self):
143
+ # """Logout and Exit"""
144
+
145
+ # try:
146
+ # self.logout()
147
+ # except Exception as e:
148
+ # logger.exception(e)
149
+ # finally:
150
+ # sys.exit()
151
+
152
+ # ----------------------------------------------------------------------------------------------
153
+
154
+ def get_version(self) -> str | None:
155
+ """Get version"""
156
+
157
+ info: str = "Get version"
158
+
159
+ try:
160
+
161
+ logger.info(f"{info} [started]")
162
+
163
+ data = self.request('apiinfo.version', [])
164
+
165
+ if data is None:
166
+ return None
167
+
168
+ logger.success(f"{info} [succeeded]")
169
+ return data['result']
170
+
171
+ except Exception as e:
172
+ logger.error(f"{info} [failed]")
173
+ logger.exception(e)
174
+ return None
175
+
176
+ # ----------------------------------------------------------------------------------------------
177
+
178
+ def get_ids_by_template_name(self, name: str) -> list | None:
179
+ """Get ids by template name"""
180
+
181
+ # https://www.zabbix.com/documentation/7.0/en/manual/api/reference/template
182
+ # name: string/array
183
+ # example: 'Linux by Zabbix agent' / ['Linux by Zabbix agent', 'Linux by Zabbix agent active']
184
+ # 如果 name 为 '' (空), 返回所有 template id
185
+
186
+ info: str = "Get ids by template name"
187
+
188
+ try:
189
+
190
+ logger.info(f"{info} [started]")
191
+
192
+ data = self.request('template.get', {'output': 'templateid', 'filter': {'name': name}})
193
+
194
+ if data is None:
195
+ return None
196
+
197
+ if not utils.isTrue(data['result'], list):
198
+ logger.error(f"{info} [error: {data['error']}]")
199
+ return None
200
+
201
+ logger.success(f"{info} [succeeded]")
202
+ return [i['templateid'] for i in data['result']]
203
+
204
+ except Exception as e:
205
+ logger.error(f"{info} [failed]")
206
+ logger.exception(e)
207
+ return None
208
+
209
+ # ----------------------------------------------------------------------------------------------
210
+
211
+ def get_ids_by_hostgroup_name(self, name: str) -> list | None:
212
+ """Get ids by hostgroup name"""
213
+
214
+ # # https://www.zabbix.com/documentation/7.0/en/manual/api/reference/hostgroup
215
+ # name: string/array
216
+ # example: 'Linux servers' / ['Linux servers', 'Discovered hosts']
217
+ # 如果 name 为 '' (空), 返回所有 hostgroup id
218
+
219
+ info: str = "Get ids by hostgroup name"
220
+
221
+ try:
222
+
223
+ logger.info(f"{info} [started]")
224
+
225
+ # Zabbix 6.0 -> output: groupid
226
+ data = self.request('hostgroup.get', {'output': 'extend', 'filter': {'name': name}})
227
+
228
+ if data is None:
229
+ return None
230
+
231
+ if not utils.isTrue(data.get('result'), list):
232
+ logger.error(f"{info} [error: {data['error']}]")
233
+ return None
234
+
235
+ logger.success(f"{info} [succeeded]")
236
+ return [i['groupid'] for i in data['result']]
237
+
238
+ except Exception as e:
239
+ logger.error(f"{info} [failed]")
240
+ logger.exception(e)
241
+ return None
242
+
243
+ # ----------------------------------------------------------------------------------------------
244
+
245
+ def get_hosts_by_template_name(self, name: str, output: str = 'extend', **kwargs) -> list | None:
246
+ """Get hosts by template name"""
247
+
248
+ # name: string/array
249
+ # example: 'Linux by Zabbix agent' / ['Linux by Zabbix agent', 'Linux by Zabbix agent active']
250
+ # 如果 name 为 '' (空), 返回所有 host
251
+
252
+ info: str = "Get hosts by template name"
253
+
254
+ try:
255
+
256
+ logger.info(f"{info} [started]")
257
+
258
+ templates = self.request('template.get', {'output': ['templateid'], 'filter': {'host': name}})
259
+
260
+ if templates is None:
261
+ return None
262
+
263
+ if not utils.isTrue(templates.get('result'), list):
264
+ logger.error(f"{info} [error: {templates['error']}]")
265
+ return None
266
+
267
+ templateids = [i['templateid'] for i in templates['result']]
268
+
269
+ hosts = self.request('host.get', {'output': output, 'templateids': templateids, **kwargs})
270
+
271
+ if hosts is None:
272
+ return None
273
+
274
+ if not utils.isTrue(hosts.get('result'), list):
275
+ logger.error(f"{info} [error: {hosts['error']}]")
276
+ return None
277
+
278
+ logger.success(f"{info} [succeeded]")
279
+ return hosts['result']
280
+
281
+ except Exception as e:
282
+ logger.error(f"{info} [failed]")
283
+ logger.exception(e)
284
+ return None
285
+
286
+ # ----------------------------------------------------------------------------------------------
287
+
288
+ def get_hosts_by_hostgroup_name(self, name: str, output: str | list = 'extend', **kwargs) -> list | None:
289
+ """Get hosts by hostgroup name"""
290
+
291
+ # name: string/array
292
+ # example: 'Linux servers' / ['Linux servers', 'Discovered hosts']
293
+ # 如果 name 为 '' (空), 返回所有 hosts
294
+
295
+ info: str = "Get hosts by hostgroup name"
296
+
297
+ try:
298
+
299
+ logger.info(f"{info} [started]")
300
+
301
+ ids = self.get_ids_by_hostgroup_name(name)
302
+
303
+ if ids is None:
304
+ return None
305
+
306
+ hosts = self.request('host.get', {'output': output, 'groupids': ids, **kwargs})
307
+
308
+ if hosts is None:
309
+ return None
310
+
311
+ if not utils.isTrue(hosts.get('result'), list):
312
+ logger.error(f"{info} [error: {hosts['error']}]")
313
+ return None
314
+
315
+ logger.success(f"{info} [succeeded]")
316
+ return hosts['result']
317
+
318
+ except Exception as e:
319
+ logger.error(f"{info} [failed]")
320
+ logger.exception(e)
321
+ return None
322
+
323
+ # ----------------------------------------------------------------------------------------------
324
+
325
+ def get_interface_by_host_id(self, hostid: str, output: str = 'extend') -> list | None:
326
+ """Get interface by host id"""
327
+
328
+ # hostids: string/array
329
+ # example: '10792' / ['10792', '10793']
330
+
331
+ info: str = "Get interface by host id"
332
+
333
+ try:
334
+
335
+ logger.info(f"{info} [started]")
336
+
337
+ data = self.request('hostinterface.get', {'output': output, 'hostids': hostid})
338
+
339
+ if data is None:
340
+ return None
341
+
342
+ if not utils.isTrue(data.get('result'), list):
343
+ logger.error(f"{info} [error: {data['error']}]")
344
+ return None
345
+
346
+ logger.success(f"{info} [succeeded]")
347
+ return data['result']
348
+
349
+ except Exception as e:
350
+ logger.error(f"{info} [failed]")
351
+ logger.exception(e)
352
+ return None
353
+
354
+ # ----------------------------------------------------------------------------------------------
355
+
356
+ def available_hosts(self, hosts: list) -> tuple | None:
357
+ """可用服务器"""
358
+
359
+ try:
360
+
361
+ if not utils.isTrue(hosts, list):
362
+ logger.error("hosts is not a list")
363
+ return None
364
+
365
+ # 可用服务器, 不可用服务器
366
+ available, unavailable = [], []
367
+
368
+ # 服务器排查
369
+ for host in hosts:
370
+ if host['interfaces'][0]['available'] != '1':
371
+ unavailable.append(host['name'])
372
+ else:
373
+ available.append(host)
374
+
375
+ return available, unavailable
376
+
377
+ except Exception as e:
378
+ logger.exception(e)
379
+ return None
380
+
381
+ # ----------------------------------------------------------------------------------------------
382
+
383
+ def get_history_by_item_key(
384
+ self,
385
+ hosts: list,
386
+ time_from: int,
387
+ time_till: int,
388
+ item_key: str,
389
+ data_type: int = 3
390
+ ) -> list | None:
391
+ """get history by item key"""
392
+
393
+ # 1. 根据 item key 获取 item id, 通过 item id 获取 history
394
+ # 2. 根据 host 的 item id 和 history 的 item id 将数据提取为一个 history list
395
+ # 3. 根据 history list 中的 clock 排序, 然后将 history list 整合到 host 中
396
+ # 4. 返回包含有 item key, item id 和 history list 的 host 的 host list
397
+ #
398
+ # 通过 Item Key 获取 Item history
399
+ #
400
+ # hosts: 主机列表
401
+ # time_from: 开始时间
402
+ # time_till: 结束时间
403
+ # item_key: Item Key
404
+ # data_type: 数据类型
405
+ #
406
+ # 参考文档:
407
+ #
408
+ # https://www.zabbix.com/documentation/6.0/en/manual/api/reference/history/get
409
+ #
410
+ # history
411
+ #
412
+ # 0 - numeric float
413
+ # 1 - character
414
+ # 2 - log
415
+ # 3 - numeric unsigned
416
+ # 4 - text
417
+ #
418
+ # Default: 3
419
+ #
420
+ # 默认数据类型是 numeric unsigned (整数), 如果 history.get 返回的数据为 None, 有可能是 data_type 类型不对
421
+
422
+ info: str = "Get history by item key"
423
+
424
+ try:
425
+
426
+ logger.info(f"{info} [started]")
427
+
428
+ # match True:
429
+ # case True if not utils.isTrue(hosts, list):
430
+ # logger.error(f"{info} [hosts is not a list]")
431
+ # return None
432
+ # case True if not utils.isTrue(time_from, int):
433
+ # logger.error(f"{info} [time_from is not a integer]")
434
+ # return None
435
+ # case True if not utils.isTrue(time_till, int):
436
+ # logger.error(f"{info} [time_till is not a integer]")
437
+ # return None
438
+ # case True if not utils.isTrue(item_key, str):
439
+ # logger.error(f"{info} [item_key is not a string]")
440
+ # return None
441
+
442
+ # 初始化变量
443
+ # item_ids 获取历史数据时使用
444
+ # item_history 历史数据集合, 最后返回
445
+ item_ids: list = []
446
+ item_history: list = []
447
+
448
+ # Deep Copy(拷贝数据)
449
+ # 父函数的变量是 list 或者 dict 类型, 父函数将变量传递个子函数, 如果子函数对变量数据进行了修改, 那么父函数的变量的数据也会被修改
450
+ # 为了避免出现这种问题, 可以使用 Deep Copy 拷贝一份数据, 避免子函数修改父函数的变量的数据
451
+ hosts = deepcopy(hosts)
452
+
453
+ # --------------------------------------------------------------------------------------
454
+
455
+ # Get Item
456
+ hostids = [i['hostid'] for i in hosts]
457
+ item_params = {
458
+ 'output': ['name', 'itemid', 'hostid'],
459
+ 'hostids': hostids,
460
+ 'filter': {'key_': item_key}
461
+ }
462
+ items = self.request('item.get', item_params)
463
+
464
+ if items is None:
465
+ return None
466
+
467
+ # --------------------------------------------------------------------------------------
468
+
469
+ # 因为 history 获取的顺序是乱的, 为了使输出和 hosts 列表顺序一致, 将 Item ID 追加到 hosts, 然后遍历 hosts 列表输出
470
+ if not utils.isTrue(items.get('result'), list):
471
+ logger.error(f"{info} [item key {item_key} not find]")
472
+ return None
473
+
474
+ for host in hosts:
475
+ if not isinstance(items, dict):
476
+ return
477
+
478
+ item: dict = next((item_object for item_object in items['result'] if host['hostid'] == item_object['hostid']), '') # type: ignore
479
+
480
+ if utils.isTrue(item, dict) and item.get('itemid') is not None:
481
+ host['itemkey'] = item_key
482
+ host['itemid'] = item['itemid']
483
+ item_ids.append(item['itemid'])
484
+ item_history.append(host)
485
+
486
+ # 如果 ID 列表为空, 则返回 None
487
+ if not utils.isTrue(item_ids, list):
488
+ logger.error(f"{info} [item key {item_key} not find]")
489
+ return None
490
+
491
+ # --------------------------------------------------------------------------------------
492
+
493
+ # Get History
494
+ history_params = {
495
+ 'output': 'extend',
496
+ 'history': data_type,
497
+ 'itemids': item_ids,
498
+ 'time_from': time_from,
499
+ 'time_till': time_till
500
+ }
501
+ history = self.request('history.get', history_params)
502
+
503
+ if history is None:
504
+ return None
505
+
506
+ # --------------------------------------------------------------------------------------------------
507
+
508
+ if not utils.isTrue(history.get('result'), list):
509
+ logger.error(f"{info} [item history not find]")
510
+ return None
511
+
512
+ for item in item_history:
513
+ # 根据 itemid 提取数据
514
+ item_history_data = [history_result for history_result in history['result'] if item['itemid'] == history_result['itemid']]
515
+ # 根据 clock 排序
516
+ item_history_data = utils.list_dict_sorted_by_key(item_history_data, 'clock')
517
+ # 整合数据
518
+ item['history'] = item_history_data
519
+
520
+ logger.success(f"{info} [succeeded]")
521
+ return item_history
522
+
523
+ except Exception as e:
524
+ logger.error(f"{info} [failed]")
525
+ logger.exception(e)
526
+ return None
527
+
528
+ # ----------------------------------------------------------------------------------------------
529
+
530
+ def get_history_by_interface(
531
+ self,
532
+ hosts: list,
533
+ interfaces: list,
534
+ time_from: int,
535
+ time_till: int,
536
+ direction: str
537
+ ) -> list | None:
538
+ """获取网卡历史数据"""
539
+
540
+ info: str = "Get history by interface"
541
+
542
+ try:
543
+
544
+ logger.info(f"{info} [started]")
545
+
546
+ # match True:
547
+ # case True if not utils.isTrue(hosts, list):
548
+ # logger.error('ERROR!! hosts is not list or none')
549
+ # return None
550
+ # case True if not utils.isTrue(interfaces, list):
551
+ # logger.error('ERROR!! interfaces is not list or none')
552
+ # return None
553
+ # case True if not utils.isTrue(time_from, int):
554
+ # logger.error('ERROR!! time_from is not integer or zero')
555
+ # return None
556
+ # case True if not utils.isTrue(time_till, int):
557
+ # logger.error('ERROR!! time_till is not integer or zero')
558
+ # return None
559
+ # case True if not utils.isTrue(direction, str):
560
+ # logger.error('ERROR!! direction is not string or none')
561
+ # return None
562
+
563
+ # 创建一个只有 网卡名称 的 列表
564
+ interfaces_names: set = set(interface['interface'] for interface in interfaces)
565
+
566
+ # 创建一个 Key 为 网卡名称 的 dictionary
567
+ interfaces_dict: dict = {key: [] for key in interfaces_names}
568
+
569
+ # 汇集 相同网卡名称 的 IP
570
+ for interface in interfaces:
571
+ interfaces_dict[interface['interface']].append(interface['host'])
572
+
573
+ # 获取历史数据
574
+ history: list = []
575
+
576
+ for key, value in interfaces_dict.items():
577
+
578
+ hosts_by_ip = [host for v in value for host in hosts if v == host['interfaces'][0]['ip']]
579
+
580
+ data = self.get_history_by_item_key(
581
+ hosts=hosts_by_ip,
582
+ time_from=time_from,
583
+ time_till=time_till,
584
+ item_key=f'net.if.{direction}["{key}"]',
585
+ data_type=3
586
+ )
587
+
588
+ if data is None:
589
+ continue
590
+
591
+ history += data
592
+
593
+ logger.success(f"{info} [succeeded]")
594
+
595
+ # 根据 name 排序
596
+ return utils.list_dict_sorted_by_key(history, 'name')
597
+
598
+ except Exception as e:
599
+ logger.error(f"{info} [failed]")
600
+ logger.exception(e)
601
+ return None
602
+
603
+ # ----------------------------------------------------------------------------------------------
604
+
605
+ def get_ips_by_hostgroup_name(self, hostgroup_name: str) -> list | None:
606
+ """Get ips by hostgroup name"""
607
+
608
+ info: str = "Get ips by hostgroup name"
609
+
610
+ try:
611
+
612
+ logger.info(f"{info} [started]")
613
+
614
+ hosts = self.get_hosts_by_hostgroup_name(hostgroup_name)
615
+
616
+ if hosts is None:
617
+ return None
618
+
619
+ hostids = [i["hostid"] for i in hosts]
620
+
621
+ hostinterface = self.request(method="hostinterface.get", params={"hostids": hostids})
622
+
623
+ if hostinterface is None:
624
+ return None
625
+
626
+ logger.success(f"{info} [succeeded]")
627
+ return [i["ip"] for i in hostinterface.get("result", [])]
628
+
629
+ except Exception as e:
630
+ logger.error(f"{info} [failed]")
631
+ logger.exception(e)
632
+ return None
633
+
634
+ # ----------------------------------------------------------------------------------------------
635
+
636
+ def create_object(
637
+ self,
638
+ ips: list,
639
+ item: dict | None = None,
640
+ trigger: dict | None = None,
641
+ graph: bool | dict = False
642
+ ) -> bool:
643
+ """create object"""
644
+
645
+ # 创建对象
646
+ #
647
+ # ips: IP列表
648
+ #
649
+ # item:
650
+ #
651
+ # name
652
+ # key_
653
+ #
654
+ # trigger:
655
+ #
656
+ # description
657
+ # expression (必须包含 {host}, 用于定义HOST)
658
+ #
659
+ # 参考文档:
660
+ #
661
+ # https://www.zabbix.com/documentation/6.0/en/manual/api/reference/item/object
662
+ # https://www.zabbix.com/documentation/6.0/en/manual/config/items/itemtypes/zabbix_agent
663
+ # https://www.zabbix.com/documentation/6.0/en/manual/api/reference/trigger/object
664
+ #
665
+ # type:
666
+ #
667
+ # 0 - Zabbix agent
668
+ # 2 - Zabbix trapper
669
+ # 3 - Simple check
670
+ # 5 - Zabbix internal
671
+ # 7 - Zabbix agent (active)
672
+ # 9 - Web item
673
+ # 10 - External check
674
+ # 11 - Database monitor
675
+ # 12 - IPMI agent
676
+ # 13 - SSH agent
677
+ # 14 - Telnet agent
678
+ # 15 - Calculated
679
+ # 16 - JMX agent
680
+ # 17 - SNMP trap
681
+ # 18 - Dependent item
682
+ # 19 - HTTP agent
683
+ # 20 - SNMP agent
684
+ # 21 - Script
685
+ #
686
+ # value_type:
687
+ #
688
+ # 0 - numeric float
689
+ # 1 - character
690
+ # 2 - log
691
+ # 3 - numeric unsigned
692
+ # 4 - text
693
+ #
694
+ # priority (integer): Severity of the trigger
695
+ #
696
+ # 0 - (default) not classified
697
+ # 1 - information
698
+ # 2 - warning
699
+ # 3 - average
700
+ # 4 - high
701
+ # 5 - disaster
702
+
703
+ info: str = "Create object"
704
+
705
+ try:
706
+
707
+ logger.info(f"{info} [started]")
708
+
709
+ if not utils.isTrue(ips, list):
710
+ logger.error(f"{info} [ips is not a list]")
711
+ return False
712
+
713
+ for ip in ips:
714
+
715
+ item_id: str | None = None
716
+
717
+ # ----------------------------------------------------------------------------------
718
+
719
+ # Host Object
720
+
721
+ logger.info(f"{info} [get host object]")
722
+
723
+ response = self.request('hostinterface.get', {'filter': {'ip': ip}, 'selectHosts': ['host']})
724
+
725
+ if response is None:
726
+ continue
727
+
728
+ # match True:
729
+ # case True if utils.isTrue(response, dict) and utils.isTrue(response.get('result'), list):
730
+ # logger.success(f"{info} [get host object] success: {response['result'][0]['hosts'][0]['host']}")
731
+ # case True if utils.isTrue(response, dict) and response.get('error'):
732
+ # logger.error(f"{info} [get host object] error: {response.get('error', {}).get('data')}")
733
+ # continue
734
+ # case _:
735
+ # logger.error(f"{info} [get host object] error: {ip}")
736
+ # continue
737
+
738
+ host = response['result'][0]['hosts'][0]['host']
739
+ host_id = response['result'][0]['hostid']
740
+ interface_id = response['result'][0]['interfaceid']
741
+
742
+ # ----------------------------------------------------------------------------------
743
+
744
+ # Create Item
745
+
746
+ if isinstance(item, dict) and utils.isTrue(item, dict):
747
+
748
+ logger.info(f'{info} [create item]')
749
+
750
+ params = {
751
+ # 'name': None,
752
+ # 'key_': None,
753
+ 'hostid': host_id,
754
+ 'type': 7,
755
+ 'value_type': 3,
756
+ 'interfaceid': interface_id,
757
+ 'delay': '1m',
758
+ 'history': '7d',
759
+ 'trends': '7d'
760
+ } | item
761
+
762
+ response = self.request('item.create', params)
763
+
764
+ if response is None or response.get('result') is None:
765
+ continue
766
+
767
+ # match True:
768
+ # case True if utils.isTrue(response, dict) and response.get('result'):
769
+ # logger.success(f"{log_prefix} success: {response.get('result')}")
770
+ # case True if utils.isTrue(response, dict) and response.get('error'):
771
+ # logger.error(f"{log_prefix} error: {response.get('error', {}).get('data')}")
772
+ # continue
773
+ # case True if utils.isTrue(response, utils.NoneType):
774
+ # logger.error(f"{log_prefix} error: {response.get('error', {}).get('data')}")
775
+ # continue
776
+ # case _:
777
+ # logger.error(f"{log_prefix} error: {item.get('name')}")
778
+ # continue
779
+
780
+ item_id = response['result']['itemids'][0]
781
+
782
+ # ----------------------------------------------------------------------------------
783
+
784
+ # Create Trigger
785
+
786
+ if isinstance(trigger, dict) and utils.isTrue(trigger, dict):
787
+
788
+ logger.info(f'{info} [create trigger]')
789
+
790
+ params = {
791
+ # 'description': None,
792
+ 'priority': '2',
793
+ # 'expression': None,
794
+ 'manual_close': '1'
795
+ } | trigger
796
+
797
+ # Trigger 的 expression 需要指定 HOST, 例如:
798
+ # 'last(/DIYCL-110-30/system.uptime)<10m'
799
+ # DIYCL-110-30 就是 HOST
800
+ # 但是 HOST 是根据 IP 调用接口获取的, 所以可以写成动态的配置
801
+ # 'last(/{host}/system.uptime)<10m'.format(host='DIYCL-110-30')
802
+ # 所以, 传递参数的时候, expression 中就必须要有 {host}, 用于定义 HOST
803
+ # 如果传递参数的时候使用了 f-strings, 要保留 {host}, 再套一层 {} 即可
804
+ # f'last(/{{host}}/system.uptime)<10m'
805
+ params['expression'] = f"{params['expression']}".format(host=host)
806
+
807
+ # 注意: create trigger 的 params 的类型是 list
808
+ response = self.request('trigger.create', [params])
809
+
810
+ if response is None or response.get('result') is None:
811
+ continue
812
+
813
+ # logger.warning(f'{log_prefix} response: {response}') if utils.isTrue(self.debug, bool) else next
814
+
815
+ # match True:
816
+ # case True if utils.isTrue(response, dict) and response.get('result'):
817
+ # logger.success(f"{log_prefix} success: {response.get('result')}")
818
+ # case True if utils.isTrue(response, dict) and response.get('error'):
819
+ # logger.error(f"{log_prefix} error: {response.get('error', {}).get('data')}")
820
+ # continue
821
+ # case True if utils.isTrue(response, utils.NoneType):
822
+ # logger.error(f"{log_prefix} error: {response.get('error', {}).get('data')}")
823
+ # continue
824
+ # case _:
825
+ # logger.error(f"{log_prefix} error: {trigger.get('name')}")
826
+ # continue
827
+
828
+ # ----------------------------------------------------------------------------------
829
+
830
+ # Create Graph
831
+
832
+ if utils.isTrue(graph, bool) or (isinstance(graph, dict) and utils.isTrue(graph, dict)):
833
+
834
+ log_prefix = 'create graph'
835
+
836
+ logger.info(f'{log_prefix} ......')
837
+
838
+ # Graph object:
839
+ #
840
+ # https://www.zabbix.com/documentation/current/en/manual/api/reference/graph/object
841
+ #
842
+ # yaxismax (float) The fixed maximum value for the Y axis.
843
+ # Default: 100.
844
+ # yaxismin (float) The fixed minimum value for the Y axis.
845
+ # Default: 0.
846
+ # ymax_type (integer) Maximum value calculation method for the Y axis.
847
+ # Possible values:
848
+ # 0 - (default) calculated;
849
+ # 1 - fixed;
850
+ # 2 - item.
851
+ # ymin_type (integer) Minimum value calculation method for the Y axis.
852
+ # Possible values:
853
+ # 0 - (default) calculated;
854
+ # 1 - fixed;
855
+ # 2 - item.
856
+ #
857
+ # 'ymin_type': 2,
858
+ # 'ymin_itemid':item_id,
859
+ # 'ymax_type': 2,
860
+ # 'ymax_itemid':item_id,
861
+
862
+ if item is None:
863
+ continue
864
+
865
+ params: dict = {
866
+ 'name': item.get('name'),
867
+ 'width': 900,
868
+ 'height': 200,
869
+ 'gitems': [{'itemid': item_id, 'color': '0040FF'}]
870
+ }
871
+
872
+ if isinstance(graph, dict) and utils.isTrue(graph, dict):
873
+
874
+ params = params | graph
875
+
876
+ if utils.isTrue(params.get('gitems'), list):
877
+ gitems = params.get('gitems')
878
+ if gitems is None:
879
+ continue
880
+ for gitem in gitems:
881
+ if isinstance(gitem, dict) and utils.isTrue(gitem, dict) and gitem.get('itemid') == '{}':
882
+ gitem['itemid'] = item_id
883
+
884
+ response = self.request('graph.create', params)
885
+
886
+ if response is None:
887
+ continue
888
+
889
+ # logger.warning(f'{log_prefix} response: {response}') if utils.isTrue(self.debug, bool) else next
890
+
891
+ # match True:
892
+ # case True if utils.isTrue(response, dict) and response.get('result'):
893
+ # logger.success(f"{log_prefix} success: {response.get('result')}")
894
+ # case True if utils.isTrue(response, dict) and response.get('error'):
895
+ # logger.error(f"{log_prefix} error: {response.get('error', {}).get('data')}")
896
+ # continue
897
+ # case True if utils.isTrue(response, utils.NoneType):
898
+ # logger.error(f"{log_prefix} error: {response.get('error', {}).get('data')}")
899
+ # continue
900
+ # case _:
901
+ # logger.error(f"{log_prefix} error: {params.get('name')}")
902
+ # continue
903
+
904
+ # ----------------------------------------------------------------------------------
905
+
906
+ logger.success(f"{info} [succeeded]")
907
+ return True
908
+
909
+ except Exception as e:
910
+ logger.error(f"{info} [failed]")
911
+ logger.exception(e)
912
+ return False
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: ezKit
3
- Version: 1.11.15
3
+ Version: 1.11.17
4
4
  Summary: Easy Kit
5
5
  Author: septvean
6
6
  Author-email: septvean@gmail.com
@@ -8,6 +8,7 @@ ezKit/bottle.py
8
8
  ezKit/bottle_extensions.py
9
9
  ezKit/cipher.py
10
10
  ezKit/database.py
11
+ ezKit/dockerhub.py
11
12
  ezKit/http.py
12
13
  ezKit/markdown_to_html.template
13
14
  ezKit/mongo.py
@@ -17,6 +18,7 @@ ezKit/sendemail.py
17
18
  ezKit/token.py
18
19
  ezKit/utils.py
19
20
  ezKit/xftp.py
21
+ ezKit/zabbix.py
20
22
  ezKit.egg-info/PKG-INFO
21
23
  ezKit.egg-info/SOURCES.txt
22
24
  ezKit.egg-info/dependency_links.txt
@@ -3,7 +3,7 @@ from setuptools import find_packages, setup
3
3
 
4
4
  setup(
5
5
  name='ezKit',
6
- version='1.11.15',
6
+ version='1.11.17',
7
7
  author='septvean',
8
8
  author_email='septvean@gmail.com',
9
9
  description='Easy Kit',
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes