pytbox 0.0.1__py3-none-any.whl → 0.3.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pytbox might be problematic. Click here for more details.
- pytbox/alert/alert_handler.py +139 -0
- pytbox/alert/ping.py +24 -0
- pytbox/alicloud/sls.py +9 -14
- pytbox/base.py +121 -0
- pytbox/categraf/build_config.py +143 -0
- pytbox/categraf/instances.toml +39 -0
- pytbox/categraf/jinja2/__init__.py +6 -0
- pytbox/categraf/jinja2/input.cpu/cpu.toml.j2 +5 -0
- pytbox/categraf/jinja2/input.disk/disk.toml.j2 +11 -0
- pytbox/categraf/jinja2/input.diskio/diskio.toml.j2 +6 -0
- pytbox/categraf/jinja2/input.dns_query/dns_query.toml.j2 +12 -0
- pytbox/categraf/jinja2/input.http_response/http_response.toml.j2 +9 -0
- pytbox/categraf/jinja2/input.mem/mem.toml.j2 +5 -0
- pytbox/categraf/jinja2/input.net/net.toml.j2 +11 -0
- pytbox/categraf/jinja2/input.net_response/net_response.toml.j2 +9 -0
- pytbox/categraf/jinja2/input.ping/ping.toml.j2 +11 -0
- pytbox/categraf/jinja2/input.prometheus/prometheus.toml.j2 +12 -0
- pytbox/categraf/jinja2/input.snmp/cisco_interface.toml.j2 +96 -0
- pytbox/categraf/jinja2/input.snmp/cisco_system.toml.j2 +41 -0
- pytbox/categraf/jinja2/input.snmp/h3c_interface.toml.j2 +96 -0
- pytbox/categraf/jinja2/input.snmp/h3c_system.toml.j2 +41 -0
- pytbox/categraf/jinja2/input.snmp/huawei_interface.toml.j2 +96 -0
- pytbox/categraf/jinja2/input.snmp/huawei_system.toml.j2 +41 -0
- pytbox/categraf/jinja2/input.snmp/ruijie_interface.toml.j2 +96 -0
- pytbox/categraf/jinja2/input.snmp/ruijie_system.toml.j2 +41 -0
- pytbox/categraf/jinja2/input.vsphere/vsphere.toml.j2 +211 -0
- pytbox/cli/__init__.py +7 -0
- pytbox/cli/categraf/__init__.py +7 -0
- pytbox/cli/categraf/commands.py +55 -0
- pytbox/cli/commands/vm.py +22 -0
- pytbox/cli/common/__init__.py +6 -0
- pytbox/cli/common/options.py +42 -0
- pytbox/cli/common/utils.py +269 -0
- pytbox/cli/formatters/__init__.py +7 -0
- pytbox/cli/formatters/output.py +155 -0
- pytbox/cli/main.py +24 -0
- pytbox/cli.py +9 -0
- pytbox/database/mongo.py +99 -0
- pytbox/database/victoriametrics.py +404 -0
- pytbox/dida365.py +11 -17
- pytbox/excel.py +64 -0
- pytbox/feishu/endpoints.py +12 -9
- pytbox/{logger.py → log/logger.py} +78 -30
- pytbox/{victorialog.py → log/victorialog.py} +2 -2
- pytbox/mail/alimail.py +142 -0
- pytbox/mail/client.py +171 -0
- pytbox/mail/mail_detail.py +30 -0
- pytbox/mingdao.py +164 -0
- pytbox/network/meraki.py +537 -0
- pytbox/notion.py +731 -0
- pytbox/pyjira.py +612 -0
- pytbox/utils/cronjob.py +79 -0
- pytbox/utils/env.py +2 -2
- pytbox/utils/load_config.py +132 -0
- pytbox/utils/load_vm_devfile.py +45 -0
- pytbox/utils/response.py +1 -1
- pytbox/utils/richutils.py +31 -0
- pytbox/utils/timeutils.py +479 -14
- pytbox/vmware.py +120 -0
- pytbox/win/ad.py +30 -0
- {pytbox-0.0.1.dist-info → pytbox-0.3.1.dist-info}/METADATA +13 -3
- pytbox-0.3.1.dist-info/RECORD +72 -0
- pytbox-0.3.1.dist-info/entry_points.txt +2 -0
- pytbox/common/base.py +0 -0
- pytbox/victoriametrics.py +0 -37
- pytbox-0.0.1.dist-info/RECORD +0 -21
- {pytbox-0.0.1.dist-info → pytbox-0.3.1.dist-info}/WHEEL +0 -0
- {pytbox-0.0.1.dist-info → pytbox-0.3.1.dist-info}/top_level.txt +0 -0
pytbox/utils/env.py
CHANGED
|
@@ -17,14 +17,14 @@ def get_env_by_file_exist(file_path: str) -> Literal['prod', 'dev']:
|
|
|
17
17
|
return 'dev'
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
def get_env_by_os_environment() -> Literal['dev', 'prod']:
|
|
20
|
+
def get_env_by_os_environment(check_key: str='ENV') -> Literal['dev', 'prod']:
|
|
21
21
|
'''
|
|
22
22
|
根据环境变量获取环境
|
|
23
23
|
|
|
24
24
|
Returns:
|
|
25
25
|
Literal['dev', 'prod']: 环境,dev 表示开发环境,prod 表示生产环境
|
|
26
26
|
'''
|
|
27
|
-
if os.getenv(
|
|
27
|
+
if os.getenv(check_key) == 'dev':
|
|
28
28
|
return 'dev'
|
|
29
29
|
else:
|
|
30
30
|
return 'prod'
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import json
|
|
5
|
+
|
|
6
|
+
try:
|
|
7
|
+
import toml
|
|
8
|
+
except ImportError:
|
|
9
|
+
import tomllib as toml # Python 3.11+ fallback
|
|
10
|
+
from pytbox.onepassword_connect import OnePasswordConnect
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _replace_values(data, oc=None, jsonfile_path=None):
|
|
14
|
+
"""
|
|
15
|
+
递归处理配置数据,替换 1password、password 和 jsonfile 开头的值
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
data: 配置数据(dict, list, str 等)
|
|
19
|
+
oc: OnePasswordConnect 实例
|
|
20
|
+
jsonfile_path: JSON 文件路径
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
处理后的数据
|
|
24
|
+
"""
|
|
25
|
+
if isinstance(data, dict):
|
|
26
|
+
result = {}
|
|
27
|
+
for k, v in data.items():
|
|
28
|
+
result[k] = _replace_values(v, oc, jsonfile_path)
|
|
29
|
+
return result
|
|
30
|
+
elif isinstance(data, list):
|
|
31
|
+
return [_replace_values(item, oc, jsonfile_path) for item in data]
|
|
32
|
+
elif isinstance(data, str):
|
|
33
|
+
# 处理 1password,item_id,field_name 格式
|
|
34
|
+
if data.startswith("1password,") and oc:
|
|
35
|
+
parts = data.split(",")
|
|
36
|
+
if len(parts) >= 3:
|
|
37
|
+
item_id = parts[1]
|
|
38
|
+
field_name = parts[2]
|
|
39
|
+
try:
|
|
40
|
+
# 通过 item_id 获取项目,然后从字段中提取对应值
|
|
41
|
+
item = oc.get_item(item_id)
|
|
42
|
+
for field in item.fields:
|
|
43
|
+
if field.label == field_name:
|
|
44
|
+
return field.value
|
|
45
|
+
except (AttributeError, KeyError, ValueError):
|
|
46
|
+
pass
|
|
47
|
+
return data # 如果找不到字段,返回原始值
|
|
48
|
+
# 处理 password,item_id,field_name 格式
|
|
49
|
+
elif data.startswith("password,") and oc:
|
|
50
|
+
parts = data.split(",")
|
|
51
|
+
if len(parts) >= 3:
|
|
52
|
+
item_id = parts[1]
|
|
53
|
+
field_name = parts[2]
|
|
54
|
+
try:
|
|
55
|
+
# 通过 item_id 获取项目,然后从字段中提取对应值
|
|
56
|
+
item = oc.get_item(item_id)
|
|
57
|
+
for field in item.fields:
|
|
58
|
+
if field.label == field_name:
|
|
59
|
+
return field.value
|
|
60
|
+
except (AttributeError, KeyError, ValueError):
|
|
61
|
+
pass
|
|
62
|
+
return data # 如果找不到字段,返回原始值
|
|
63
|
+
# 处理 jsonfile,key 格式
|
|
64
|
+
elif data.startswith("jsonfile,"):
|
|
65
|
+
parts = data.split(",", 1) # 只分割一次,防止 key 中包含逗号
|
|
66
|
+
if len(parts) >= 2:
|
|
67
|
+
key = parts[1]
|
|
68
|
+
|
|
69
|
+
# 尝试从 JSON 文件获取值
|
|
70
|
+
if jsonfile_path and os.path.exists(jsonfile_path):
|
|
71
|
+
try:
|
|
72
|
+
with open(jsonfile_path, 'r', encoding='utf-8') as f:
|
|
73
|
+
json_data = json.load(f)
|
|
74
|
+
# 支持嵌套键,如 "db.password"
|
|
75
|
+
value = json_data
|
|
76
|
+
for k in key.split('.'):
|
|
77
|
+
if isinstance(value, dict) and k in value:
|
|
78
|
+
value = value[k]
|
|
79
|
+
else:
|
|
80
|
+
value = None
|
|
81
|
+
break
|
|
82
|
+
if value is not None:
|
|
83
|
+
return value
|
|
84
|
+
except (json.JSONDecodeError, FileNotFoundError, KeyError):
|
|
85
|
+
pass
|
|
86
|
+
|
|
87
|
+
# 如果从 JSON 文件获取失败,尝试从环境变量获取
|
|
88
|
+
env_value = os.getenv(key)
|
|
89
|
+
if env_value is not None:
|
|
90
|
+
return env_value
|
|
91
|
+
|
|
92
|
+
return data # 如果都获取不到,返回原始值
|
|
93
|
+
return data
|
|
94
|
+
else:
|
|
95
|
+
return data
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def load_config_by_file(
|
|
99
|
+
path: str='/workspaces/pytbox/src/pytbox/alert/config/config.toml',
|
|
100
|
+
oc_vault_id: str=None,
|
|
101
|
+
jsonfile: str="/data/jsonfile.json",
|
|
102
|
+
) -> dict:
|
|
103
|
+
'''
|
|
104
|
+
从文件加载配置,支持 1password 集成
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
path (str, optional): 配置文件路径. Defaults to '/workspaces/pytbox/src/pytbox/alert/config/config.toml'.
|
|
108
|
+
oc_vault_id (str, optional): OnePassword vault ID,如果提供则启用 1password 集成
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
dict: 配置字典
|
|
112
|
+
'''
|
|
113
|
+
with open(path, 'r', encoding='utf-8') as f:
|
|
114
|
+
if path.endswith('.toml'):
|
|
115
|
+
config = toml.load(f)
|
|
116
|
+
else:
|
|
117
|
+
# 如果不是 toml 文件,假设是其他格式,这里可以扩展
|
|
118
|
+
config = json.load(f)
|
|
119
|
+
|
|
120
|
+
# 处理配置值替换
|
|
121
|
+
oc = None
|
|
122
|
+
if oc_vault_id:
|
|
123
|
+
oc = OnePasswordConnect(vault_id=oc_vault_id)
|
|
124
|
+
|
|
125
|
+
# 替换配置中的特殊值(1password, password, jsonfile)
|
|
126
|
+
config = _replace_values(config, oc, jsonfile)
|
|
127
|
+
|
|
128
|
+
return config
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
if __name__ == "__main__":
|
|
132
|
+
print(load_config_by_file(path='/workspaces/pytbox/tests/alert/config_dev.toml', oc_vault_id="hcls5uxuq5dmxorw6rfewefdsa"))
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pytbox.utils.response import ReturnResponse
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def load_dev_file(file_path: str) -> ReturnResponse:
|
|
8
|
+
"""从开发环境文件加载数据并返回 ReturnResponse 对象
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
file_path: JSON 文件路径
|
|
12
|
+
|
|
13
|
+
Returns:
|
|
14
|
+
ReturnResponse: 包装后的响应对象
|
|
15
|
+
"""
|
|
16
|
+
try:
|
|
17
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
18
|
+
data = json.load(f)
|
|
19
|
+
|
|
20
|
+
# 如果已经是 ReturnResponse 格式的字典,直接转换
|
|
21
|
+
if isinstance(data, dict) and 'code' in data and 'msg' in data:
|
|
22
|
+
return ReturnResponse(
|
|
23
|
+
code=data.get('code', 0),
|
|
24
|
+
msg=data.get('msg', ''),
|
|
25
|
+
data=data.get('data', None)
|
|
26
|
+
)
|
|
27
|
+
else:
|
|
28
|
+
# 如果是其他格式,包装成成功响应
|
|
29
|
+
return ReturnResponse(
|
|
30
|
+
code=0,
|
|
31
|
+
msg='开发文件加载成功',
|
|
32
|
+
data=data
|
|
33
|
+
)
|
|
34
|
+
except FileNotFoundError:
|
|
35
|
+
return ReturnResponse(
|
|
36
|
+
code=4,
|
|
37
|
+
msg=f'开发文件未找到: {file_path}',
|
|
38
|
+
data=None
|
|
39
|
+
)
|
|
40
|
+
except json.JSONDecodeError as e:
|
|
41
|
+
return ReturnResponse(
|
|
42
|
+
code=1,
|
|
43
|
+
msg=f'JSON 解析错误: {str(e)}',
|
|
44
|
+
data=None
|
|
45
|
+
)
|
pytbox/utils/response.py
CHANGED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
from typing import Literal
|
|
5
|
+
from rich.console import Console
|
|
6
|
+
from rich.theme import Theme
|
|
7
|
+
from rich.prompt import Prompt
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class RichUtils:
|
|
11
|
+
|
|
12
|
+
def __init__(self):
|
|
13
|
+
self.theme = Theme({
|
|
14
|
+
"info": "bold blue",
|
|
15
|
+
"warning": "bold yellow",
|
|
16
|
+
"danger": "bold red",
|
|
17
|
+
})
|
|
18
|
+
self.console = Console(theme=self.theme)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def print(self, msg: str, style: Literal['info', 'warning', 'danger']='info'):
|
|
22
|
+
self.console.print(msg, style=style)
|
|
23
|
+
|
|
24
|
+
def ask(self, msg: str="是否继续操作?", choices: list[str]=["Y", "N", "CANCEL"], default: str='N', show_choices: bool=True):
|
|
25
|
+
choice = Prompt.ask(
|
|
26
|
+
f"[bold cyan]{msg}[/bold cyan]",
|
|
27
|
+
choices=choices,
|
|
28
|
+
default=default,
|
|
29
|
+
show_choices=show_choices,
|
|
30
|
+
)
|
|
31
|
+
return choice
|