ezKit 1.11.16__py3-none-any.whl → 1.11.17__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
ezKit/dockerhub.py CHANGED
@@ -46,3 +46,30 @@ def get_latest_tags(url: str, limit: int = 20, proxies: dict | None = None) -> l
46
46
  logger.error(f"{info} [error]")
47
47
  logger.exception(e)
48
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))
ezKit/zabbix.py ADDED
@@ -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.16
3
+ Version: 1.11.17
4
4
  Summary: Easy Kit
5
5
  Author: septvean
6
6
  Author-email: septvean@gmail.com
@@ -4,7 +4,7 @@ ezKit/bottle.py,sha256=43h4v1kzz6qrLvCt5IMN0H-gFtaT0koG9wETqteXsps,181666
4
4
  ezKit/bottle_extensions.py,sha256=CwXKxVKxxtbyfeeOSp2xODUqJBo7ro2C88H9sUOVDJI,1161
5
5
  ezKit/cipher.py,sha256=0T_StbjiNI4zgrjVgcfU-ffKgu1waBA9UDudAnqFcNM,2896
6
6
  ezKit/database.py,sha256=r5YNoEzeOeVTlEWI99xXtHTmPZ73_DopS8DTzZk8Lts,12432
7
- ezKit/dockerhub.py,sha256=cdcb_WCCpKyarfObNdGyhUJwe5THX6EASoTbbXFSar0,1255
7
+ ezKit/dockerhub.py,sha256=5B8vCS5mKJbTZTWJcj_-CbIzaOrfDG3tTpbbEJ1b8mA,1957
8
8
  ezKit/http.py,sha256=ysXzqXFi9zmuVKINbYGwmf9Q5xDVW_DZWrSh6HSVq8M,1800
9
9
  ezKit/markdown_to_html.template,sha256=21G2sSVGJn6aJvHd0NN4zY5YiDteKe4UtW36AzBwSdk,22274
10
10
  ezKit/mongo.py,sha256=l3jRMmoGrTm16OG4daSCn0JLU1nbYAmTtHokwjLXzoA,2390
@@ -14,8 +14,9 @@ ezKit/sendemail.py,sha256=47JTDFoLJKi0YtF3RAp9nFfo0ko2jlde3R_C1wr2E2w,7397
14
14
  ezKit/token.py,sha256=HKREyZj_T2S8-aFoFIrBXTaCKExQq4zE66OHXhGHqQg,1750
15
15
  ezKit/utils.py,sha256=uOUOCgx6WU6J2lTbHlL78Flk3oCZgdj8rBOFg2i0K7Q,44241
16
16
  ezKit/xftp.py,sha256=izUH9pLH_AzgR3c0g8xSfhLn7LQ9EDcTst3LFjTM6hU,7878
17
- ezKit-1.11.16.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
18
- ezKit-1.11.16.dist-info/METADATA,sha256=cPz1PkmJ5Bw1VqXebRUgumtjSTI_qyW1LLiwgRZQ02g,295
19
- ezKit-1.11.16.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
20
- ezKit-1.11.16.dist-info/top_level.txt,sha256=aYLB_1WODsqNTsTFWcKP-BN0KCTKcV-HZJ4zlHkCFw8,6
21
- ezKit-1.11.16.dist-info/RECORD,,
17
+ ezKit/zabbix.py,sha256=JELOKZMXOWSdq5NW-VNJ10vahhRWLAILDGebMi8VunM,33060
18
+ ezKit-1.11.17.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
19
+ ezKit-1.11.17.dist-info/METADATA,sha256=jEBrfNGf93E_-OwUiLID1MvTOisrazgv2ZfPC-j-xV0,295
20
+ ezKit-1.11.17.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
21
+ ezKit-1.11.17.dist-info/top_level.txt,sha256=aYLB_1WODsqNTsTFWcKP-BN0KCTKcV-HZJ4zlHkCFw8,6
22
+ ezKit-1.11.17.dist-info/RECORD,,