pytbox 0.1.0__py3-none-any.whl → 0.1.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/base.py +19 -3
- pytbox/categraf/build_config.py +26 -23
- pytbox/categraf/instances.toml +2 -1
- 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/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/mail/alimail.py +129 -0
- pytbox/mail/client.py +221 -0
- pytbox/network/meraki.py +169 -0
- pytbox/win/ad.py +30 -0
- {pytbox-0.1.0.dist-info → pytbox-0.1.1.dist-info}/METADATA +4 -3
- {pytbox-0.1.0.dist-info → pytbox-0.1.1.dist-info}/RECORD +18 -8
- {pytbox-0.1.0.dist-info → pytbox-0.1.1.dist-info}/WHEEL +0 -0
- {pytbox-0.1.0.dist-info → pytbox-0.1.1.dist-info}/entry_points.txt +0 -0
- {pytbox-0.1.0.dist-info → pytbox-0.1.1.dist-info}/top_level.txt +0 -0
pytbox/base.py
CHANGED
|
@@ -8,11 +8,11 @@ from pytbox.feishu.client import Client as FeishuClient
|
|
|
8
8
|
from pytbox.dida365 import Dida365
|
|
9
9
|
from pytbox.alert.alert_handler import AlertHandler
|
|
10
10
|
from pytbox.log.logger import AppLogger
|
|
11
|
-
|
|
11
|
+
from pytbox.win.ad import ADClient
|
|
12
|
+
from pytbox.network.meraki import Meraki
|
|
12
13
|
|
|
13
14
|
config = load_config_by_file(path='/workspaces/pytbox/tests/alert/config_dev.toml', oc_vault_id=os.environ.get('oc_vault_id'))
|
|
14
15
|
|
|
15
|
-
|
|
16
16
|
def get_mongo(collection):
|
|
17
17
|
return Mongo(
|
|
18
18
|
host=config['mongo']['host'],
|
|
@@ -45,4 +45,20 @@ def get_logger(app):
|
|
|
45
45
|
feishu=feishu,
|
|
46
46
|
dida=dida,
|
|
47
47
|
mongo=get_mongo('alert_program')
|
|
48
|
-
)
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# ad_dev = ADClient(
|
|
51
|
+
# server=config['ad']['dev']['AD_SERVER'],
|
|
52
|
+
# base_dn=config['ad']['dev']['BASE_DN'],
|
|
53
|
+
# username=config['ad']['dev']['AD_USERNAME'],
|
|
54
|
+
# password=config['ad']['dev']['AD_PASSWORD']
|
|
55
|
+
# )
|
|
56
|
+
|
|
57
|
+
# ad_prod = ADClient(
|
|
58
|
+
# server=config['ad']['prod']['AD_SERVER'],
|
|
59
|
+
# base_dn=config['ad']['prod']['BASE_DN'],
|
|
60
|
+
# username=config['ad']['prod']['AD_USERNAME'],
|
|
61
|
+
# password=config['ad']['prod']['AD_PASSWORD']
|
|
62
|
+
# )
|
|
63
|
+
|
|
64
|
+
meraki = Meraki(api_key=config['meraki']['api_key'], organization_id=config['meraki']['organization_id'])
|
pytbox/categraf/build_config.py
CHANGED
|
@@ -41,33 +41,36 @@ class BuildConfig:
|
|
|
41
41
|
f.write(render_data)
|
|
42
42
|
|
|
43
43
|
def ping(self):
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
target_dir.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
44
|
+
if self.instances.get('ping'):
|
|
45
|
+
instances = self.instances['ping']['instance']
|
|
46
|
+
render_data = ping_template.render(instances=instances)
|
|
47
|
+
target_dir = Path(self.output_dir) / 'input.ping'
|
|
48
|
+
if not target_dir.exists():
|
|
49
|
+
target_dir.mkdir(parents=True, exist_ok=True)
|
|
50
|
+
|
|
51
|
+
with open(Path(self.output_dir) / 'input.ping' / 'ping.toml', 'w', encoding='utf-8') as f:
|
|
52
|
+
f.write(render_data)
|
|
52
53
|
|
|
53
54
|
def prometheus(self):
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
target_dir.
|
|
59
|
-
|
|
60
|
-
|
|
55
|
+
if self.instances.get('prometheus'):
|
|
56
|
+
instances = self.instances['prometheus']['urls']
|
|
57
|
+
render_data = prometheus_template.render(instances=instances)
|
|
58
|
+
target_dir = Path(self.output_dir) / 'input.prometheus'
|
|
59
|
+
if not target_dir.exists():
|
|
60
|
+
target_dir.mkdir(parents=True, exist_ok=True)
|
|
61
|
+
with open(Path(self.output_dir) / 'input.prometheus' / 'prometheus.toml', 'w', encoding='utf-8') as f:
|
|
62
|
+
f.write(render_data)
|
|
61
63
|
|
|
62
64
|
def vsphere(self):
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
target_dir.
|
|
69
|
-
|
|
70
|
-
|
|
65
|
+
if self.instances.get('vsphere'):
|
|
66
|
+
template = self._get_template('input.vsphere/vsphere.toml.j2')
|
|
67
|
+
instances = self.instances['vsphere']['instance']
|
|
68
|
+
render_data = template.render(instances=instances)
|
|
69
|
+
target_dir = Path(self.output_dir) / 'input.vsphere'
|
|
70
|
+
if not target_dir.exists():
|
|
71
|
+
target_dir.mkdir(parents=True, exist_ok=True)
|
|
72
|
+
with open(Path(self.output_dir) / 'input.vsphere' / 'vsphere.toml', 'w', encoding='utf-8') as f:
|
|
73
|
+
f.write(render_data)
|
|
71
74
|
|
|
72
75
|
def http_response(self):
|
|
73
76
|
template = self._get_template('input.http_response/http_response.toml.j2')
|
pytbox/categraf/instances.toml
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
[prometheus]
|
|
7
7
|
[[prometheus.urls]]
|
|
8
|
-
"http://10.
|
|
8
|
+
"http://10.1.1.1:9100" = { name = "x", env = "prod" }
|
|
9
9
|
|
|
10
10
|
[vsphere]
|
|
11
11
|
[[vsphere.instance]]
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"119.29.29.29_baidu.com" = { dns_server = "119.29.29.29", domains = "www.baidu.com", labels = { name = "x", env = "prod" } }
|
|
26
26
|
|
|
27
27
|
[snmp]
|
|
28
|
+
# 支持 h3c,huawei,cisco,ruijie
|
|
28
29
|
[[snmp.instances.h3c]]
|
|
29
30
|
"10.1.1.1:161" = { version = 2, community = "public" }
|
|
30
31
|
"10.1.1.2:161" = { version = 2, community = "public" }
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
{% for instance in instances %}
|
|
2
|
+
{% for agent, detail in instance.items() %}
|
|
3
|
+
[[instances]]
|
|
4
|
+
agents = [
|
|
5
|
+
"udp://{{agent}}"
|
|
6
|
+
]
|
|
7
|
+
{% if detail.version == 2 %}
|
|
8
|
+
version = {{detail.version}}
|
|
9
|
+
community = "{{detail.community}}"
|
|
10
|
+
{% endif %}
|
|
11
|
+
{% if detail.version == 3 %}
|
|
12
|
+
version = {{detail.version}}
|
|
13
|
+
sec_name = "{{detail.sec_name}}"
|
|
14
|
+
auth_protocol = "{{detail.auth_protocol}}"
|
|
15
|
+
auth_password = "{{detail.auth_password}}"
|
|
16
|
+
priv_protocol = "{{detail.priv_protocol}}"
|
|
17
|
+
priv_password = "{{detail.priv_password}}"
|
|
18
|
+
sec_level = "{{detail.sec_level}}"
|
|
19
|
+
{% endif %}
|
|
20
|
+
timeout = "5s"
|
|
21
|
+
retries = 3
|
|
22
|
+
path = ["/usr/share/snmp/mibs"]
|
|
23
|
+
translator = "gosmi"
|
|
24
|
+
max_repetitions = 50
|
|
25
|
+
|
|
26
|
+
[[instances.field]]
|
|
27
|
+
name = "sysName"
|
|
28
|
+
oid = "1.3.6.1.2.1.1.5.0"
|
|
29
|
+
is_tag = true
|
|
30
|
+
|
|
31
|
+
[[instances.table]]
|
|
32
|
+
name = "interface"
|
|
33
|
+
inherit_tags = ["sysName"]
|
|
34
|
+
index_as_tag = true
|
|
35
|
+
|
|
36
|
+
[[instances.table.field]]
|
|
37
|
+
name = "ifIndex"
|
|
38
|
+
oid = "1.3.6.1.2.1.2.2.1.1"
|
|
39
|
+
is_tag = true
|
|
40
|
+
|
|
41
|
+
[[instances.table.field]]
|
|
42
|
+
name = "ifName"
|
|
43
|
+
oid = "1.3.6.1.2.1.31.1.1.1.1"
|
|
44
|
+
is_tag = true
|
|
45
|
+
|
|
46
|
+
[[instances.table.field]]
|
|
47
|
+
name = "ifDescr"
|
|
48
|
+
oid = "1.3.6.1.2.1.2.2.1.2"
|
|
49
|
+
is_tag = true
|
|
50
|
+
|
|
51
|
+
[[instances.table.field]]
|
|
52
|
+
name = "ifSpeed"
|
|
53
|
+
oid = "1.3.6.1.2.1.2.2.1.5"
|
|
54
|
+
# is_tag = true
|
|
55
|
+
|
|
56
|
+
[[instances.table.field]]
|
|
57
|
+
name = "ifAdminStatus"
|
|
58
|
+
oid = "1.3.6.1.2.1.2.2.1.7"
|
|
59
|
+
# is_tag = true
|
|
60
|
+
|
|
61
|
+
[[instances.table.field]]
|
|
62
|
+
name = "ifOperStatus"
|
|
63
|
+
oid = "1.3.6.1.2.1.2.2.1.8"
|
|
64
|
+
# is_tag = true
|
|
65
|
+
|
|
66
|
+
[[instances.table.field]]
|
|
67
|
+
name = "ifInDiscards"
|
|
68
|
+
oid = "1.3.6.1.2.1.2.2.1.13"
|
|
69
|
+
|
|
70
|
+
[[instances.table.field]]
|
|
71
|
+
name = "ifInErrors"
|
|
72
|
+
oid = "1.3.6.1.2.1.2.2.1.14"
|
|
73
|
+
|
|
74
|
+
[[instances.table.field]]
|
|
75
|
+
name = "ifOutDiscards"
|
|
76
|
+
oid = "1.3.6.1.2.1.2.2.1.19"
|
|
77
|
+
|
|
78
|
+
[[instances.table.field]]
|
|
79
|
+
name = "ifOutErrors"
|
|
80
|
+
oid = "1.3.6.1.2.1.2.2.1.20"
|
|
81
|
+
|
|
82
|
+
[[instances.table.field]]
|
|
83
|
+
name = "ifAlias"
|
|
84
|
+
oid = "1.3.6.1.2.1.31.1.1.1.18"
|
|
85
|
+
is_tag = true
|
|
86
|
+
|
|
87
|
+
[[instances.table.field]]
|
|
88
|
+
name = "ifHCInOctets"
|
|
89
|
+
oid = "1.3.6.1.2.1.31.1.1.1.6"
|
|
90
|
+
|
|
91
|
+
[[instances.table.field]]
|
|
92
|
+
name = "ifHCOutOctets"
|
|
93
|
+
oid = "1.3.6.1.2.1.31.1.1.1.10"
|
|
94
|
+
|
|
95
|
+
{% endfor %}
|
|
96
|
+
{% endfor %}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{% for instance in instances %}
|
|
2
|
+
{% for agent, detail in instance.items() %}
|
|
3
|
+
[[instances]]
|
|
4
|
+
agents = [
|
|
5
|
+
"udp://{{agent}}"
|
|
6
|
+
]
|
|
7
|
+
{% if detail.version == 2 %}
|
|
8
|
+
version = {{detail.version}}
|
|
9
|
+
community = "{{detail.community}}"
|
|
10
|
+
{% endif %}
|
|
11
|
+
{% if detail.version == 3 %}
|
|
12
|
+
version = {{detail.version}}
|
|
13
|
+
sec_name = "{{detail.sec_name}}"
|
|
14
|
+
auth_protocol = "{{detail.auth_protocol}}"
|
|
15
|
+
auth_password = "{{detail.auth_password}}"
|
|
16
|
+
priv_protocol = "{{detail.priv_protocol}}"
|
|
17
|
+
priv_password = "{{detail.priv_password}}"
|
|
18
|
+
sec_level = "{{detail.sec_level}}"
|
|
19
|
+
{% endif %}
|
|
20
|
+
timeout = "5s"
|
|
21
|
+
retries = 3
|
|
22
|
+
path = ["/usr/share/snmp/mibs"]
|
|
23
|
+
translator = "gosmi"
|
|
24
|
+
max_repetitions = 50
|
|
25
|
+
|
|
26
|
+
[[instances.field]]
|
|
27
|
+
name = "sysName"
|
|
28
|
+
oid = "1.3.6.1.2.1.1.5.0"
|
|
29
|
+
is_tag = true
|
|
30
|
+
|
|
31
|
+
[[instances.field]]
|
|
32
|
+
oid = "1.3.6.1.2.1.1.1.0"
|
|
33
|
+
name = "sysDescr"
|
|
34
|
+
is_tag = true
|
|
35
|
+
|
|
36
|
+
[[instances.field]]
|
|
37
|
+
oid = "1.3.6.1.2.1.1.3.0"
|
|
38
|
+
name = "sysUpTime"
|
|
39
|
+
conversion = "float(2)"
|
|
40
|
+
{% endfor %}
|
|
41
|
+
{% endfor %}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
{% for instance in instances %}
|
|
2
|
+
{% for agent, detail in instance.items() %}
|
|
3
|
+
[[instances]]
|
|
4
|
+
agents = [
|
|
5
|
+
"udp://{{agent}}"
|
|
6
|
+
]
|
|
7
|
+
{% if detail.version == 2 %}
|
|
8
|
+
version = {{detail.version}}
|
|
9
|
+
community = "{{detail.community}}"
|
|
10
|
+
{% endif %}
|
|
11
|
+
{% if detail.version == 3 %}
|
|
12
|
+
version = {{detail.version}}
|
|
13
|
+
sec_name = "{{detail.sec_name}}"
|
|
14
|
+
auth_protocol = "{{detail.auth_protocol}}"
|
|
15
|
+
auth_password = "{{detail.auth_password}}"
|
|
16
|
+
priv_protocol = "{{detail.priv_protocol}}"
|
|
17
|
+
priv_password = "{{detail.priv_password}}"
|
|
18
|
+
sec_level = "{{detail.sec_level}}"
|
|
19
|
+
{% endif %}
|
|
20
|
+
timeout = "5s"
|
|
21
|
+
retries = 3
|
|
22
|
+
path = ["/usr/share/snmp/mibs"]
|
|
23
|
+
translator = "gosmi"
|
|
24
|
+
max_repetitions = 50
|
|
25
|
+
|
|
26
|
+
[[instances.field]]
|
|
27
|
+
name = "sysName"
|
|
28
|
+
oid = "1.3.6.1.2.1.1.5.0"
|
|
29
|
+
is_tag = true
|
|
30
|
+
|
|
31
|
+
[[instances.table]]
|
|
32
|
+
name = "interface"
|
|
33
|
+
inherit_tags = ["sysName"]
|
|
34
|
+
index_as_tag = true
|
|
35
|
+
|
|
36
|
+
[[instances.table.field]]
|
|
37
|
+
name = "ifIndex"
|
|
38
|
+
oid = "1.3.6.1.2.1.2.2.1.1"
|
|
39
|
+
is_tag = true
|
|
40
|
+
|
|
41
|
+
[[instances.table.field]]
|
|
42
|
+
name = "ifName"
|
|
43
|
+
oid = "1.3.6.1.2.1.31.1.1.1.1"
|
|
44
|
+
is_tag = true
|
|
45
|
+
|
|
46
|
+
[[instances.table.field]]
|
|
47
|
+
name = "ifDescr"
|
|
48
|
+
oid = "1.3.6.1.2.1.2.2.1.2"
|
|
49
|
+
is_tag = true
|
|
50
|
+
|
|
51
|
+
[[instances.table.field]]
|
|
52
|
+
name = "ifSpeed"
|
|
53
|
+
oid = "1.3.6.1.2.1.2.2.1.5"
|
|
54
|
+
# is_tag = true
|
|
55
|
+
|
|
56
|
+
[[instances.table.field]]
|
|
57
|
+
name = "ifAdminStatus"
|
|
58
|
+
oid = "1.3.6.1.2.1.2.2.1.7"
|
|
59
|
+
# is_tag = true
|
|
60
|
+
|
|
61
|
+
[[instances.table.field]]
|
|
62
|
+
name = "ifOperStatus"
|
|
63
|
+
oid = "1.3.6.1.2.1.2.2.1.8"
|
|
64
|
+
# is_tag = true
|
|
65
|
+
|
|
66
|
+
[[instances.table.field]]
|
|
67
|
+
name = "ifInDiscards"
|
|
68
|
+
oid = "1.3.6.1.2.1.2.2.1.13"
|
|
69
|
+
|
|
70
|
+
[[instances.table.field]]
|
|
71
|
+
name = "ifInErrors"
|
|
72
|
+
oid = "1.3.6.1.2.1.2.2.1.14"
|
|
73
|
+
|
|
74
|
+
[[instances.table.field]]
|
|
75
|
+
name = "ifOutDiscards"
|
|
76
|
+
oid = "1.3.6.1.2.1.2.2.1.19"
|
|
77
|
+
|
|
78
|
+
[[instances.table.field]]
|
|
79
|
+
name = "ifOutErrors"
|
|
80
|
+
oid = "1.3.6.1.2.1.2.2.1.20"
|
|
81
|
+
|
|
82
|
+
[[instances.table.field]]
|
|
83
|
+
name = "ifAlias"
|
|
84
|
+
oid = "1.3.6.1.2.1.31.1.1.1.18"
|
|
85
|
+
is_tag = true
|
|
86
|
+
|
|
87
|
+
[[instances.table.field]]
|
|
88
|
+
name = "ifHCInOctets"
|
|
89
|
+
oid = "1.3.6.1.2.1.31.1.1.1.6"
|
|
90
|
+
|
|
91
|
+
[[instances.table.field]]
|
|
92
|
+
name = "ifHCOutOctets"
|
|
93
|
+
oid = "1.3.6.1.2.1.31.1.1.1.10"
|
|
94
|
+
|
|
95
|
+
{% endfor %}
|
|
96
|
+
{% endfor %}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{% for instance in instances %}
|
|
2
|
+
{% for agent, detail in instance.items() %}
|
|
3
|
+
[[instances]]
|
|
4
|
+
agents = [
|
|
5
|
+
"udp://{{agent}}"
|
|
6
|
+
]
|
|
7
|
+
{% if detail.version == 2 %}
|
|
8
|
+
version = {{detail.version}}
|
|
9
|
+
community = "{{detail.community}}"
|
|
10
|
+
{% endif %}
|
|
11
|
+
{% if detail.version == 3 %}
|
|
12
|
+
version = {{detail.version}}
|
|
13
|
+
sec_name = "{{detail.sec_name}}"
|
|
14
|
+
auth_protocol = "{{detail.auth_protocol}}"
|
|
15
|
+
auth_password = "{{detail.auth_password}}"
|
|
16
|
+
priv_protocol = "{{detail.priv_protocol}}"
|
|
17
|
+
priv_password = "{{detail.priv_password}}"
|
|
18
|
+
sec_level = "{{detail.sec_level}}"
|
|
19
|
+
{% endif %}
|
|
20
|
+
timeout = "5s"
|
|
21
|
+
retries = 3
|
|
22
|
+
path = ["/usr/share/snmp/mibs"]
|
|
23
|
+
translator = "gosmi"
|
|
24
|
+
max_repetitions = 50
|
|
25
|
+
|
|
26
|
+
[[instances.field]]
|
|
27
|
+
name = "sysName"
|
|
28
|
+
oid = "1.3.6.1.2.1.1.5.0"
|
|
29
|
+
is_tag = true
|
|
30
|
+
|
|
31
|
+
[[instances.field]]
|
|
32
|
+
oid = "1.3.6.1.2.1.1.1.0"
|
|
33
|
+
name = "sysDescr"
|
|
34
|
+
is_tag = true
|
|
35
|
+
|
|
36
|
+
[[instances.field]]
|
|
37
|
+
oid = "1.3.6.1.2.1.1.3.0"
|
|
38
|
+
name = "sysUpTime"
|
|
39
|
+
conversion = "float(2)"
|
|
40
|
+
{% endfor %}
|
|
41
|
+
{% endfor %}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
{% for instance in instances %}
|
|
2
|
+
{% for agent, detail in instance.items() %}
|
|
3
|
+
[[instances]]
|
|
4
|
+
agents = [
|
|
5
|
+
"udp://{{agent}}"
|
|
6
|
+
]
|
|
7
|
+
{% if detail.version == 2 %}
|
|
8
|
+
version = {{detail.version}}
|
|
9
|
+
community = "{{detail.community}}"
|
|
10
|
+
{% endif %}
|
|
11
|
+
{% if detail.version == 3 %}
|
|
12
|
+
version = {{detail.version}}
|
|
13
|
+
sec_name = "{{detail.sec_name}}"
|
|
14
|
+
auth_protocol = "{{detail.auth_protocol}}"
|
|
15
|
+
auth_password = "{{detail.auth_password}}"
|
|
16
|
+
priv_protocol = "{{detail.priv_protocol}}"
|
|
17
|
+
priv_password = "{{detail.priv_password}}"
|
|
18
|
+
sec_level = "{{detail.sec_level}}"
|
|
19
|
+
{% endif %}
|
|
20
|
+
timeout = "5s"
|
|
21
|
+
retries = 3
|
|
22
|
+
path = ["/usr/share/snmp/mibs"]
|
|
23
|
+
translator = "gosmi"
|
|
24
|
+
max_repetitions = 50
|
|
25
|
+
|
|
26
|
+
[[instances.field]]
|
|
27
|
+
name = "sysName"
|
|
28
|
+
oid = "1.3.6.1.2.1.1.5.0"
|
|
29
|
+
is_tag = true
|
|
30
|
+
|
|
31
|
+
[[instances.table]]
|
|
32
|
+
name = "interface"
|
|
33
|
+
inherit_tags = ["sysName"]
|
|
34
|
+
index_as_tag = true
|
|
35
|
+
|
|
36
|
+
[[instances.table.field]]
|
|
37
|
+
name = "ifIndex"
|
|
38
|
+
oid = "1.3.6.1.2.1.2.2.1.1"
|
|
39
|
+
is_tag = true
|
|
40
|
+
|
|
41
|
+
[[instances.table.field]]
|
|
42
|
+
name = "ifName"
|
|
43
|
+
oid = "1.3.6.1.2.1.31.1.1.1.1"
|
|
44
|
+
is_tag = true
|
|
45
|
+
|
|
46
|
+
[[instances.table.field]]
|
|
47
|
+
name = "ifDescr"
|
|
48
|
+
oid = "1.3.6.1.2.1.2.2.1.2"
|
|
49
|
+
is_tag = true
|
|
50
|
+
|
|
51
|
+
[[instances.table.field]]
|
|
52
|
+
name = "ifSpeed"
|
|
53
|
+
oid = "1.3.6.1.2.1.2.2.1.5"
|
|
54
|
+
# is_tag = true
|
|
55
|
+
|
|
56
|
+
[[instances.table.field]]
|
|
57
|
+
name = "ifAdminStatus"
|
|
58
|
+
oid = "1.3.6.1.2.1.2.2.1.7"
|
|
59
|
+
# is_tag = true
|
|
60
|
+
|
|
61
|
+
[[instances.table.field]]
|
|
62
|
+
name = "ifOperStatus"
|
|
63
|
+
oid = "1.3.6.1.2.1.2.2.1.8"
|
|
64
|
+
# is_tag = true
|
|
65
|
+
|
|
66
|
+
[[instances.table.field]]
|
|
67
|
+
name = "ifInDiscards"
|
|
68
|
+
oid = "1.3.6.1.2.1.2.2.1.13"
|
|
69
|
+
|
|
70
|
+
[[instances.table.field]]
|
|
71
|
+
name = "ifInErrors"
|
|
72
|
+
oid = "1.3.6.1.2.1.2.2.1.14"
|
|
73
|
+
|
|
74
|
+
[[instances.table.field]]
|
|
75
|
+
name = "ifOutDiscards"
|
|
76
|
+
oid = "1.3.6.1.2.1.2.2.1.19"
|
|
77
|
+
|
|
78
|
+
[[instances.table.field]]
|
|
79
|
+
name = "ifOutErrors"
|
|
80
|
+
oid = "1.3.6.1.2.1.2.2.1.20"
|
|
81
|
+
|
|
82
|
+
[[instances.table.field]]
|
|
83
|
+
name = "ifAlias"
|
|
84
|
+
oid = "1.3.6.1.2.1.31.1.1.1.18"
|
|
85
|
+
is_tag = true
|
|
86
|
+
|
|
87
|
+
[[instances.table.field]]
|
|
88
|
+
name = "ifHCInOctets"
|
|
89
|
+
oid = "1.3.6.1.2.1.31.1.1.1.6"
|
|
90
|
+
|
|
91
|
+
[[instances.table.field]]
|
|
92
|
+
name = "ifHCOutOctets"
|
|
93
|
+
oid = "1.3.6.1.2.1.31.1.1.1.10"
|
|
94
|
+
|
|
95
|
+
{% endfor %}
|
|
96
|
+
{% endfor %}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{% for instance in instances %}
|
|
2
|
+
{% for agent, detail in instance.items() %}
|
|
3
|
+
[[instances]]
|
|
4
|
+
agents = [
|
|
5
|
+
"udp://{{agent}}"
|
|
6
|
+
]
|
|
7
|
+
{% if detail.version == 2 %}
|
|
8
|
+
version = {{detail.version}}
|
|
9
|
+
community = "{{detail.community}}"
|
|
10
|
+
{% endif %}
|
|
11
|
+
{% if detail.version == 3 %}
|
|
12
|
+
version = {{detail.version}}
|
|
13
|
+
sec_name = "{{detail.sec_name}}"
|
|
14
|
+
auth_protocol = "{{detail.auth_protocol}}"
|
|
15
|
+
auth_password = "{{detail.auth_password}}"
|
|
16
|
+
priv_protocol = "{{detail.priv_protocol}}"
|
|
17
|
+
priv_password = "{{detail.priv_password}}"
|
|
18
|
+
sec_level = "{{detail.sec_level}}"
|
|
19
|
+
{% endif %}
|
|
20
|
+
timeout = "5s"
|
|
21
|
+
retries = 3
|
|
22
|
+
path = ["/usr/share/snmp/mibs"]
|
|
23
|
+
translator = "gosmi"
|
|
24
|
+
max_repetitions = 50
|
|
25
|
+
|
|
26
|
+
[[instances.field]]
|
|
27
|
+
name = "sysName"
|
|
28
|
+
oid = "1.3.6.1.2.1.1.5.0"
|
|
29
|
+
is_tag = true
|
|
30
|
+
|
|
31
|
+
[[instances.field]]
|
|
32
|
+
oid = "1.3.6.1.2.1.1.1.0"
|
|
33
|
+
name = "sysDescr"
|
|
34
|
+
is_tag = true
|
|
35
|
+
|
|
36
|
+
[[instances.field]]
|
|
37
|
+
oid = "1.3.6.1.2.1.1.3.0"
|
|
38
|
+
name = "sysUpTime"
|
|
39
|
+
conversion = "float(2)"
|
|
40
|
+
{% endfor %}
|
|
41
|
+
{% endfor %}
|
pytbox/mail/alimail.py
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
from typing import Literal
|
|
4
|
+
from datetime import datetime, timedelta
|
|
5
|
+
|
|
6
|
+
import requests
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class AliMail:
|
|
11
|
+
def __init__(self, email_address: str=None, client_id: str=None, client_secret: str=None):
|
|
12
|
+
|
|
13
|
+
self.email_address = email_address
|
|
14
|
+
self.client_id = auth['username']
|
|
15
|
+
self.client_secret = auth['password']
|
|
16
|
+
self.base_url = "https://alimail-cn.aliyuncs.com/v2"
|
|
17
|
+
self.headers = {
|
|
18
|
+
"Content-Type": "application/json",
|
|
19
|
+
"Authorization": f"bearer {self._get_access_token()}"
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
def _get_access_token(self):
|
|
23
|
+
current_time = datetime.now()
|
|
24
|
+
# stored_token = mongo_alimail_token.find_one({"token_type": "bearer"})
|
|
25
|
+
if stored_token and stored_token.get('expiration_time'):
|
|
26
|
+
expiration_time = stored_token['expiration_time']
|
|
27
|
+
if current_time < expiration_time:
|
|
28
|
+
return stored_token['access_token']
|
|
29
|
+
else:
|
|
30
|
+
return self._get_access_token_by_request()
|
|
31
|
+
else:
|
|
32
|
+
return self._get_access_token_by_request()
|
|
33
|
+
|
|
34
|
+
def _get_access_token_by_request(self):
|
|
35
|
+
'''
|
|
36
|
+
https://mailhelp.aliyun.com/openapi/index.html#/markdown/authorization.md
|
|
37
|
+
|
|
38
|
+
Raises:
|
|
39
|
+
ValueError: _description_
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
_type_: _description_
|
|
43
|
+
'''
|
|
44
|
+
# 定义接口URL
|
|
45
|
+
interface_url = "https://alimail-cn.aliyuncs.com/oauth2/v2.0/token"
|
|
46
|
+
# 设置请求头,指定内容类型
|
|
47
|
+
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
|
|
48
|
+
# 准备请求数据
|
|
49
|
+
data = {
|
|
50
|
+
"grant_type": "client_credentials",
|
|
51
|
+
"client_id": self.client_id,
|
|
52
|
+
"client_secret": self.client_secret
|
|
53
|
+
}
|
|
54
|
+
try:
|
|
55
|
+
response = requests.post(interface_url, headers=headers, data=data, timeout=3)
|
|
56
|
+
response_json = response.json()
|
|
57
|
+
current_time = datetime.now()
|
|
58
|
+
data = {
|
|
59
|
+
'token_type': response_json["token_type"],
|
|
60
|
+
'access_token': response_json["access_token"],
|
|
61
|
+
'expires_in': response_json["expires_in"],
|
|
62
|
+
'expiration_time': current_time + timedelta(seconds=response_json["expires_in"])
|
|
63
|
+
}
|
|
64
|
+
mongo_alimail_token.update_one(
|
|
65
|
+
{"token_type": "bearer"},
|
|
66
|
+
{"$set": data},
|
|
67
|
+
upsert=True
|
|
68
|
+
)
|
|
69
|
+
return data.get("access_token")
|
|
70
|
+
except requests.RequestException as e:
|
|
71
|
+
# 处理请求失败异常
|
|
72
|
+
print(f"请求失败:{e}")
|
|
73
|
+
except (KeyError, ValueError) as e:
|
|
74
|
+
# 处理解析响应失败异常
|
|
75
|
+
print(f"解析响应失败: {e}")
|
|
76
|
+
|
|
77
|
+
def list_mail_folders(self):
|
|
78
|
+
response = requests.get(
|
|
79
|
+
url=f"{self.base_url}/users/{self.email_address}/mailFolders",
|
|
80
|
+
headers=self.headers
|
|
81
|
+
)
|
|
82
|
+
return response.json().get('folders')
|
|
83
|
+
|
|
84
|
+
def query_folder_id(self, folder_name: Literal['inbox']='inbox'):
|
|
85
|
+
folders = self.list_mail_folders()
|
|
86
|
+
for folder in folders:
|
|
87
|
+
if folder.get('displayName') == folder_name:
|
|
88
|
+
return folder.get('id')
|
|
89
|
+
return None
|
|
90
|
+
|
|
91
|
+
def get_mail_detail(self, mail_id: str):
|
|
92
|
+
params = {
|
|
93
|
+
"$select": "body,toRecipients,internetMessageId,internetMessageHeaders"
|
|
94
|
+
}
|
|
95
|
+
response = requests.get(
|
|
96
|
+
url=f"{self.base_url}/users/{self.email_address}/messages/{mail_id}",
|
|
97
|
+
headers=self.headers,
|
|
98
|
+
params=params,
|
|
99
|
+
timeout=3
|
|
100
|
+
)
|
|
101
|
+
return response.json().get('message')
|
|
102
|
+
|
|
103
|
+
def list_mail(self, folder_name: str='inbox', size: int=100):
|
|
104
|
+
folder_id = self.query_folder_id(folder_name=folder_name)
|
|
105
|
+
params = {
|
|
106
|
+
"size": size,
|
|
107
|
+
# "$select": "toRecipients"
|
|
108
|
+
}
|
|
109
|
+
response = requests.get(
|
|
110
|
+
url=f"{self.base_url}/users/{self.email_address}/mailFolders/{folder_id}/messages",
|
|
111
|
+
headers=self.headers,
|
|
112
|
+
params=params,
|
|
113
|
+
timeout=3
|
|
114
|
+
)
|
|
115
|
+
messages = response.json().get("messages")
|
|
116
|
+
for message in messages:
|
|
117
|
+
mail_id = message.get("id")
|
|
118
|
+
detail = self.get_mail_detail(mail_id=mail_id)
|
|
119
|
+
message.update(detail)
|
|
120
|
+
yield message
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
if __name__ == '__main__':
|
|
124
|
+
ali_mail = AliMail()
|
|
125
|
+
# print(ali_mail.query_folder_id())
|
|
126
|
+
# for i in ali_mail.list_mail(size=1):
|
|
127
|
+
# print(i)
|
|
128
|
+
# print(ali_mail.access_token)
|
|
129
|
+
print(ali_mail.get_mail_detail(mail_id='DzzzzzzMeuY'))
|
pytbox/mail/client.py
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import Generator, Literal
|
|
6
|
+
|
|
7
|
+
import yagmail
|
|
8
|
+
from imbox import Imbox
|
|
9
|
+
from imap_tools import MailBox, AND, MailMessageFlags
|
|
10
|
+
from nb_log import get_logger
|
|
11
|
+
|
|
12
|
+
from src.library.onepassword import my1p
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
log = get_logger('src.library.mail.client')
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class MailDetail:
|
|
20
|
+
"""
|
|
21
|
+
邮件详情数据类。
|
|
22
|
+
|
|
23
|
+
Attributes:
|
|
24
|
+
uid: 邮件唯一标识符
|
|
25
|
+
send_from: 发件人邮箱地址
|
|
26
|
+
send_to: 收件人邮箱地址列表
|
|
27
|
+
cc: 抄送人邮箱地址列表
|
|
28
|
+
subject: 邮件主题
|
|
29
|
+
body_plain: 纯文本正文
|
|
30
|
+
body_html: HTML格式正文
|
|
31
|
+
attachment: 附件完整保存路径列表
|
|
32
|
+
"""
|
|
33
|
+
uid: str=None
|
|
34
|
+
send_from: str=None
|
|
35
|
+
send_to: list=None
|
|
36
|
+
date: str=None
|
|
37
|
+
cc: list=None
|
|
38
|
+
subject: str=None
|
|
39
|
+
body_plain: str=None
|
|
40
|
+
body_html: str=None
|
|
41
|
+
attachment: list=None
|
|
42
|
+
has_attachments: bool=False
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class MailClient:
|
|
46
|
+
'''
|
|
47
|
+
_summary_
|
|
48
|
+
'''
|
|
49
|
+
def __init__(self,
|
|
50
|
+
send_mail_address: Literal['service@tyun.cn', 'alert@tyun.cn', 'houmingming@tyun.cn', 'houmdream@163.com', 'houm01@foxmail.com'] = 'service@tyun.cn',
|
|
51
|
+
authorization_code: bool=False,
|
|
52
|
+
password: str=None
|
|
53
|
+
):
|
|
54
|
+
|
|
55
|
+
self.mail_address = send_mail_address
|
|
56
|
+
|
|
57
|
+
if password:
|
|
58
|
+
self.password = password
|
|
59
|
+
else:
|
|
60
|
+
if authorization_code:
|
|
61
|
+
self.password = my1p.get_item_by_title(title=send_mail_address)['授权码']
|
|
62
|
+
else:
|
|
63
|
+
self.password = my1p.get_item_by_title(title=send_mail_address)['password']
|
|
64
|
+
|
|
65
|
+
if '163.com' in send_mail_address:
|
|
66
|
+
self.smtp_address = 'smtp.163.com'
|
|
67
|
+
self.imap_address = 'imap.163.com'
|
|
68
|
+
self.imbox_client = self._create_imbox_object(vendor='163')
|
|
69
|
+
elif 'foxmail.com' in send_mail_address:
|
|
70
|
+
self.smtp_address = 'smtp.qq.com'
|
|
71
|
+
self.imap_address = 'imap.qq.com'
|
|
72
|
+
self.imbox_client = self._create_imbox_object(vendor='qq')
|
|
73
|
+
else:
|
|
74
|
+
raise ValueError(f'不支持的邮箱地址: {send_mail_address}')
|
|
75
|
+
|
|
76
|
+
self.mailbox = MailBox(self.imap_address).login(self.mail_address, self.password)
|
|
77
|
+
|
|
78
|
+
def _create_imbox_object(self, vendor: Literal['aliyun', 'qq', '163']):
|
|
79
|
+
return Imbox(self.imap_address,
|
|
80
|
+
username=self.mail_address,
|
|
81
|
+
password=self.password,
|
|
82
|
+
ssl=True,
|
|
83
|
+
ssl_context=None,
|
|
84
|
+
starttls=False)
|
|
85
|
+
|
|
86
|
+
def send_mail(self, receiver: list=[], cc: list=['houmingming@tyun.cn'], subject: str='', contents: str='', attachments: list=[], tips: str=None):
|
|
87
|
+
'''
|
|
88
|
+
_summary_
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
receiver (list, optional): _description_. Defaults to [].
|
|
92
|
+
cc (list, optional): _description_. Defaults to ['houmingming@tyun.cn'].
|
|
93
|
+
subject (str, optional): _description_. Defaults to ''.
|
|
94
|
+
contents (str, optional): _description_. Defaults to ''.
|
|
95
|
+
attachments (list, optional): _description_. Defaults to [].
|
|
96
|
+
'''
|
|
97
|
+
email_signature = """
|
|
98
|
+
————————————————————————————————————————
|
|
99
|
+
以专业成就客户
|
|
100
|
+
钛信(上海)信息科技有限公司
|
|
101
|
+
Tai Xin(ShangHai) Information Technology Co.,Ltd.
|
|
102
|
+
中国上海市浦东新区达尔文路88号半岛科技园21栋2楼
|
|
103
|
+
Tel:400-920-0057
|
|
104
|
+
Web:www.tyun.cn
|
|
105
|
+
————————————————————————————————————————
|
|
106
|
+
信息安全声明:本邮件包含信息归发件人所在组织所有,发件人所在组织对该邮件拥有所有权利。请接收者注意保密,未经发件人书面许可,不得向任何第三方组织和个人透露本邮件所含信息的全部或部分。以上声明仅适用于工作邮件。
|
|
107
|
+
Information Security Notice: The information contained in this mail is solely property of the sender's organization. This mail communication is confidential. Recipients named above are obligated to maintain secrecy and are not permitted to disclose the contents of this communication to others.
|
|
108
|
+
"""
|
|
109
|
+
with yagmail.SMTP(user=self.mail_address, password=my1p.get_item_by_title(self.mail_address)['password'], port=465, host=self.smtp_address) as yag:
|
|
110
|
+
log.info(f'receiver: {receiver}, cc: {cc}, subject: {subject}, contents: {contents}, attachments: {attachments}')
|
|
111
|
+
try:
|
|
112
|
+
if tips:
|
|
113
|
+
contents = contents + '\n' + '<p style="color: red;">本邮件为系统自动发送, 如有问题, 请联系 houmingming@tyun.cn</p>'
|
|
114
|
+
|
|
115
|
+
contents = contents + email_signature
|
|
116
|
+
yag.send(to=receiver, cc=cc, subject=subject, contents=contents, attachments=attachments)
|
|
117
|
+
log.info('发送成功!!!')
|
|
118
|
+
return True
|
|
119
|
+
except Exception as e:
|
|
120
|
+
log.error(f'发送失败, 报错: {e}')
|
|
121
|
+
return False
|
|
122
|
+
|
|
123
|
+
def get_mail_list(self, is_read: bool=False) -> Generator:
|
|
124
|
+
with MailBox(self.imap_address).login(self.mail_address, self.password) as mailbox:
|
|
125
|
+
for msg in mailbox.fetch(criteria=AND(seen=is_read)):
|
|
126
|
+
# 处理附件
|
|
127
|
+
attachment_list = []
|
|
128
|
+
for att in msg.attachments:
|
|
129
|
+
att_dict = {}
|
|
130
|
+
att_dict['filename'] = att.filename
|
|
131
|
+
att_dict['payload'] = att.payload
|
|
132
|
+
att_dict['size'] = att.size
|
|
133
|
+
att_dict['content_id'] = att.content_id
|
|
134
|
+
att_dict['content_type'] = att.content_type
|
|
135
|
+
att_dict['content_disposition'] = att.content_disposition
|
|
136
|
+
# att_dict['part'] = att.part
|
|
137
|
+
att_dict['size'] = att.size
|
|
138
|
+
attachment_list.append(att_dict)
|
|
139
|
+
|
|
140
|
+
if attachment_list:
|
|
141
|
+
is_has_attachments = True
|
|
142
|
+
else:
|
|
143
|
+
is_has_attachments = False
|
|
144
|
+
|
|
145
|
+
yield MailDetail(
|
|
146
|
+
uid=msg.uid,
|
|
147
|
+
send_from=msg.from_,
|
|
148
|
+
send_to=msg.to,
|
|
149
|
+
date=msg.date,
|
|
150
|
+
cc=msg.cc,
|
|
151
|
+
subject=msg.subject,
|
|
152
|
+
body_plain=msg.text,
|
|
153
|
+
body_html=msg.html,
|
|
154
|
+
has_attachments=is_has_attachments,
|
|
155
|
+
attachment=attachment_list
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
def mark_as_read(self, uid):
|
|
159
|
+
"""
|
|
160
|
+
标记邮件为已读。
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
uid (str): 邮件的唯一标识符
|
|
164
|
+
"""
|
|
165
|
+
try:
|
|
166
|
+
# 使用 imap_tools 的 flag 方法标记邮件为已读
|
|
167
|
+
# 第一个参数是 uid,第二个参数是要设置的标志,第三个参数 True 表示添加标志
|
|
168
|
+
self.mailbox.flag(uid, [MailMessageFlags.SEEN], True)
|
|
169
|
+
# log.info(f'标注邮件{uid}为已读')
|
|
170
|
+
except Exception as e:
|
|
171
|
+
log.error(f'标记邮件{uid}为已读失败: {e}')
|
|
172
|
+
raise
|
|
173
|
+
|
|
174
|
+
def delete(self, uid):
|
|
175
|
+
"""
|
|
176
|
+
删除邮件。
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
uid (str): 邮件的唯一标识符
|
|
180
|
+
"""
|
|
181
|
+
try:
|
|
182
|
+
# 使用 imap_tools 的 delete 方法删除邮件
|
|
183
|
+
self.mailbox.delete(uid)
|
|
184
|
+
log.info(f'删除邮件{uid}')
|
|
185
|
+
except Exception as e:
|
|
186
|
+
log.error(f'删除邮件{uid}失败: {e}')
|
|
187
|
+
raise
|
|
188
|
+
|
|
189
|
+
def move(self, uid: str, destination_folder: str) -> None:
|
|
190
|
+
"""
|
|
191
|
+
移动邮件到指定文件夹。
|
|
192
|
+
|
|
193
|
+
注意:部分 IMAP 服务商(如 QQ 邮箱)在移动邮件时,实际上是"复制到目标文件夹并从原文件夹删除",
|
|
194
|
+
这会导致邮件在原文件夹中消失,表现为"被删除"。但邮件会在目标文件夹中存在,并未彻底丢失。
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
uid (str): 邮件的唯一标识符。
|
|
198
|
+
destination_folder (str): 目标文件夹名称。
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
None
|
|
202
|
+
|
|
203
|
+
Raises:
|
|
204
|
+
Exception: 移动过程中底层 imap 库抛出的异常。
|
|
205
|
+
"""
|
|
206
|
+
try:
|
|
207
|
+
# 使用 imap_tools 的 move 方法移动邮件
|
|
208
|
+
self.mailbox.move(uid, destination_folder)
|
|
209
|
+
log.info(f'邮件{uid}已移动到{destination_folder}')
|
|
210
|
+
except Exception as e:
|
|
211
|
+
log.error(f'移动邮件{uid}到{destination_folder}失败: {e}')
|
|
212
|
+
raise
|
|
213
|
+
|
|
214
|
+
if __name__ == '__main__':
|
|
215
|
+
# ali_mail = MailClient(send_mail_address='houmingming@tyun.cn')
|
|
216
|
+
# mail = MailClient(send_mail_address='houmingming@tyun.cn')
|
|
217
|
+
# mail = MailClient(send_mail_address='houmdream@163.com', authorization_code=True)
|
|
218
|
+
# mail = MailClient(send_mail_address='houm01@foxmail.com', password=my1p.get_item_by_title('QQ'))
|
|
219
|
+
# 对于阿里云邮箱,使用兼容方法
|
|
220
|
+
pass
|
|
221
|
+
# mail.get_mail_list(attachment=True, attachment_path='/tmp')
|
pytbox/network/meraki.py
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
from typing import Any, Literal
|
|
4
|
+
import requests
|
|
5
|
+
from ..utils.response import ReturnResponse
|
|
6
|
+
|
|
7
|
+
class Meraki:
|
|
8
|
+
'''
|
|
9
|
+
Meraki Client
|
|
10
|
+
'''
|
|
11
|
+
def __init__(self, api_key: str=None, organization_id: str=None, timeout: int=10):
|
|
12
|
+
if not api_key:
|
|
13
|
+
raise ValueError("api_key is required")
|
|
14
|
+
if not organization_id:
|
|
15
|
+
raise ValueError("organization_id is required")
|
|
16
|
+
self.base_url = 'https://api.meraki.cn/api/v1'
|
|
17
|
+
self.headers = {
|
|
18
|
+
"Authorization": f"Bearer {api_key}",
|
|
19
|
+
"Accept": "application/json"
|
|
20
|
+
}
|
|
21
|
+
self.organization_id = organization_id
|
|
22
|
+
self.timeout = timeout
|
|
23
|
+
|
|
24
|
+
def get_networks(self, tags: list[str]=None) -> ReturnResponse:
|
|
25
|
+
'''
|
|
26
|
+
https://developer.cisco.com/meraki/api-v1/get-organization-networks/
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
tags (list): PROD STG NSO
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
list: _description_
|
|
33
|
+
'''
|
|
34
|
+
params = {}
|
|
35
|
+
if tags:
|
|
36
|
+
params['tags[]'] = tags
|
|
37
|
+
|
|
38
|
+
r = requests.get(
|
|
39
|
+
f"{self.base_url}/organizations/{self.organization_id}/networks",
|
|
40
|
+
headers=self.headers,
|
|
41
|
+
params=params,
|
|
42
|
+
timeout=self.timeout
|
|
43
|
+
)
|
|
44
|
+
if r.status_code == 200:
|
|
45
|
+
return ReturnResponse(code=0, msg=f"获取到 {len(r.json())} 个网络", data=r.json())
|
|
46
|
+
return ReturnResponse(code=1, msg=f"获取网络失败: {r.status_code} {r.text}")
|
|
47
|
+
|
|
48
|
+
def get_network_id_by_name(self, name: str) -> str | None:
|
|
49
|
+
'''
|
|
50
|
+
name 必须是唯一值,否则仅反馈第一个匹配到的 network
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
name (str): 网络名称, 是包含的关系, 例如实际 name 是 main office, 传入的 name 可以是 office
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
str | None: 网络ID
|
|
57
|
+
'''
|
|
58
|
+
r = self.get_networks()
|
|
59
|
+
if r.code == 0:
|
|
60
|
+
for network in r.data:
|
|
61
|
+
if name in network['name']:
|
|
62
|
+
return network['id']
|
|
63
|
+
return None
|
|
64
|
+
|
|
65
|
+
def get_devices(self, network_ids: Any = []) -> ReturnResponse:
|
|
66
|
+
'''
|
|
67
|
+
获取设备信息
|
|
68
|
+
https://developer.cisco.com/meraki/api-v1/get-organization-inventory-devices/
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
network_ids (list 或 str, 可选): 可以传入网络ID的列表,也可以直接传入单个网络ID字符串。默认为空列表,表示不指定网络ID。
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
返回示例(部分敏感信息已隐藏):
|
|
75
|
+
[
|
|
76
|
+
{
|
|
77
|
+
'mac': '00:00:00:00:00:00',
|
|
78
|
+
'serial': 'Q3AL-****-****',
|
|
79
|
+
'name': 'OFFICE-AP01',
|
|
80
|
+
'model': 'MR44',
|
|
81
|
+
'networkId': 'L_***************0076',
|
|
82
|
+
'orderNumber': None,
|
|
83
|
+
'claimedAt': '2025-02-26T02:20:00.853251Z',
|
|
84
|
+
'tags': ['MR44'],
|
|
85
|
+
'productType': 'wireless',
|
|
86
|
+
'countryCode': 'CN',
|
|
87
|
+
'details': []
|
|
88
|
+
}
|
|
89
|
+
]
|
|
90
|
+
'''
|
|
91
|
+
|
|
92
|
+
params = {}
|
|
93
|
+
if network_ids:
|
|
94
|
+
if isinstance(network_ids, str):
|
|
95
|
+
params['networkIds[]'] = [network_ids]
|
|
96
|
+
else:
|
|
97
|
+
params['networkIds[]'] = network_ids
|
|
98
|
+
|
|
99
|
+
r = requests.get(
|
|
100
|
+
f"{self.base_url}/organizations/{self.organization_id}/inventory/devices",
|
|
101
|
+
headers=self.headers,
|
|
102
|
+
params=params,
|
|
103
|
+
timeout=self.timeout
|
|
104
|
+
)
|
|
105
|
+
if r.status_code == 200:
|
|
106
|
+
return ReturnResponse(code=0, msg=f"获取到 {len(r.json())} 个设备", data=r.json())
|
|
107
|
+
return ReturnResponse(code=1, msg=f"获取设备失败: {r.status_code} {r.text}")
|
|
108
|
+
|
|
109
|
+
def get_device_detail(self, serial: str) -> ReturnResponse:
|
|
110
|
+
'''
|
|
111
|
+
获取指定序列号(serial)的 Meraki 设备详细信息
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
serial (str): 设备的序列号
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
ReturnResponse:
|
|
118
|
+
code: 0 表示成功,1 表示失败,3 表示设备未添加
|
|
119
|
+
msg: 结果说明
|
|
120
|
+
data: 设备详细信息,示例(部分敏感信息已隐藏):
|
|
121
|
+
{
|
|
122
|
+
'lat': 1.1,
|
|
123
|
+
'lng': 2.2,
|
|
124
|
+
'address': '(1.1, 2.2)',
|
|
125
|
+
'serial': 'Q3AL-****-****',
|
|
126
|
+
'mac': '00:00:00:00:00:00',
|
|
127
|
+
'lanIp': '10.1.1.1',
|
|
128
|
+
'tags': ['MR44'],
|
|
129
|
+
'url': 'https://n3.meraki.cn/xxx',
|
|
130
|
+
'networkId': '00000',
|
|
131
|
+
'name': 'OFFICE-AP01',
|
|
132
|
+
'details': [],
|
|
133
|
+
'model': 'MR44',
|
|
134
|
+
'firmware': 'wireless-31-1-6',
|
|
135
|
+
'floorPlanId': '00000'
|
|
136
|
+
}
|
|
137
|
+
'''
|
|
138
|
+
r = requests.get(
|
|
139
|
+
f"{self.base_url}/devices/{serial}",
|
|
140
|
+
headers=self.headers,
|
|
141
|
+
timeout=self.timeout
|
|
142
|
+
)
|
|
143
|
+
if r.status_code == 200:
|
|
144
|
+
return ReturnResponse(code=0, msg=f"获取设备详情成功: {r.json()}", data=r.json())
|
|
145
|
+
elif r.status_code == 404:
|
|
146
|
+
return ReturnResponse(code=3, msg=f"设备 {serial} 还未添加过", data=None)
|
|
147
|
+
return ReturnResponse(code=1, msg=f"获取设备详情失败: {r.status_code} - {r.text}", data=None)
|
|
148
|
+
|
|
149
|
+
def get_device_availabilities(self, network_id: str=None,
|
|
150
|
+
status: Literal['online', 'offline', 'dormant', 'alerting']=None,
|
|
151
|
+
serial: str=None) -> ReturnResponse:
|
|
152
|
+
params = {}
|
|
153
|
+
|
|
154
|
+
if status:
|
|
155
|
+
params["statuses[]"] = status
|
|
156
|
+
|
|
157
|
+
if serial:
|
|
158
|
+
params["serials[]"] = serial
|
|
159
|
+
|
|
160
|
+
if network_id:
|
|
161
|
+
params["networkIds[]"] = network_id
|
|
162
|
+
|
|
163
|
+
r = requests.get(
|
|
164
|
+
url=f"{self.base_url}/organizations/{self.organization_id}/devices/availabilities",
|
|
165
|
+
headers=self.headers,
|
|
166
|
+
params=params,
|
|
167
|
+
timeout=self.timeout
|
|
168
|
+
)
|
|
169
|
+
return ReturnResponse(code=0, msg=f"获取设备健康状态成功", data=r.json())
|
pytbox/win/ad.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
from ldap3 import Server, Connection, ALL, SUBTREE, MODIFY_REPLACE, SUBTREE, MODIFY_ADD, MODIFY_DELETE
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ADClient:
|
|
8
|
+
'''
|
|
9
|
+
_summary_
|
|
10
|
+
'''
|
|
11
|
+
def __init__(self, server, base_dn, username, password):
|
|
12
|
+
self.server = Server(server, get_info=ALL)
|
|
13
|
+
self.conn = Connection(self.server, user=username, password=password, auto_bind=True)
|
|
14
|
+
self.base_dn = base_dn
|
|
15
|
+
|
|
16
|
+
def list_user(self):
|
|
17
|
+
'''
|
|
18
|
+
查询所有用户
|
|
19
|
+
|
|
20
|
+
Yields:
|
|
21
|
+
dict: 返回的是生成器, 字典类型
|
|
22
|
+
'''
|
|
23
|
+
# 搜索过滤条件
|
|
24
|
+
secarch_filter = '(objectCategory=person)' # 过滤所有用户
|
|
25
|
+
# SEARCH_ATTRIBUTES = ['cn', 'sAMAccountName', 'mail', 'userPrincipalName'] # 需要的用户属性
|
|
26
|
+
search_attributes = ["*"] # 需要的用户属性
|
|
27
|
+
# 搜索用户
|
|
28
|
+
if self.conn.search(search_base=self.base_dn, search_filter=secarch_filter, search_scope=SUBTREE, attributes=search_attributes):
|
|
29
|
+
for entry in self.conn.entries:
|
|
30
|
+
yield {k: v[0] if isinstance(v, list) else v for k, v in entry.entry_attributes_as_dict.items()}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pytbox
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: A collection of Python integrations and utilities (Feishu, Dida365, VictoriaMetrics, ...)
|
|
5
5
|
Author-email: mingming hou <houm01@foxmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -13,15 +13,16 @@ Requires-Dist: loguru>=0.7.3
|
|
|
13
13
|
Requires-Dist: chinese_calendar>=1.10.0
|
|
14
14
|
Requires-Dist: click>=8.0.0
|
|
15
15
|
Requires-Dist: rich>=12.0.0
|
|
16
|
+
Requires-Dist: jinja2>=3.0.0
|
|
17
|
+
Requires-Dist: toml>=0.10.0
|
|
18
|
+
Requires-Dist: ldap3>=2.9.1
|
|
16
19
|
Provides-Extra: dev
|
|
17
20
|
Requires-Dist: pytest; extra == "dev"
|
|
18
21
|
Requires-Dist: black; extra == "dev"
|
|
19
22
|
Requires-Dist: ruff; extra == "dev"
|
|
20
23
|
Requires-Dist: python-dotenv; extra == "dev"
|
|
21
24
|
Provides-Extra: cli
|
|
22
|
-
Requires-Dist: jinja2>=3.0.0; extra == "cli"
|
|
23
25
|
Requires-Dist: pyyaml>=6.0; extra == "cli"
|
|
24
|
-
Requires-Dist: toml>=0.10.0; extra == "cli"
|
|
25
26
|
|
|
26
27
|
# PytBox
|
|
27
28
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
pytbox/base.py,sha256=
|
|
1
|
+
pytbox/base.py,sha256=rt2vs_h6bLXggnXp6WTywEBgq_Wg0-hdTEG5BzxPJWU,2116
|
|
2
2
|
pytbox/cli.py,sha256=N775a0GK80IT2lQC2KRYtkZpIiu9UjavZmaxgNUgJhQ,160
|
|
3
3
|
pytbox/dida365.py,sha256=pUMPB9AyLZpTTbaz2LbtzdEpyjvuGf4YlRrCvM5sbJo,10545
|
|
4
4
|
pytbox/onepassword_connect.py,sha256=nD3xTl1ykQ4ct_dCRRF138gXCtk-phPfKYXuOn-P7Z8,3064
|
|
@@ -6,8 +6,8 @@ pytbox/onepassword_sa.py,sha256=08iUcYud3aEHuQcUsem9bWNxdXKgaxFbMy9yvtr-DZQ,6995
|
|
|
6
6
|
pytbox/alert/alert_handler.py,sha256=FePPQS4LyGphSJ0QMv0_pLWaXxEqsRlcTKMfUjtsNfk,5048
|
|
7
7
|
pytbox/alert/ping.py,sha256=g36X0U3U8ndZqfpVIcuoxJJ0X5gST3I_IwjTQC1roHA,779
|
|
8
8
|
pytbox/alicloud/sls.py,sha256=UR4GdI86dCKAFI2xt_1DELu7q743dpd3xrYtuNpfC5A,4065
|
|
9
|
-
pytbox/categraf/build_config.py,sha256=
|
|
10
|
-
pytbox/categraf/instances.toml,sha256=
|
|
9
|
+
pytbox/categraf/build_config.py,sha256=Php9Y0K_5RBr3jtgZuJYaBVpx0Uo-kXK9VvCNQkQR_Q,6541
|
|
10
|
+
pytbox/categraf/instances.toml,sha256=Nq3yQIeCa2fZw4jr1g9B9WjUexiURk0tK02SHPwTei0,1193
|
|
11
11
|
pytbox/categraf/jinja2/__init__.py,sha256=Epm01j8Oujeg4Sk5GgHMvgKIZ6h3BTx-MGmuMgIjUMo,150
|
|
12
12
|
pytbox/categraf/jinja2/input.cpu/cpu.toml.j2,sha256=wxpyLDNvz2ViZK-0a4EgH35Zsg82CwMwijOtT0nBfTI,92
|
|
13
13
|
pytbox/categraf/jinja2/input.disk/disk.toml.j2,sha256=kOWwVF6u7x2huLVa4eBIOhC8R13DXmw0PD9o3HXwEZo,420
|
|
@@ -19,8 +19,14 @@ pytbox/categraf/jinja2/input.net/net.toml.j2,sha256=yodVoT1f7bcuNMFFmAvc7WhORUsT
|
|
|
19
19
|
pytbox/categraf/jinja2/input.net_response/net_response.toml.j2,sha256=Qp1EOxx7TC7_cH69KqMVwbfCKrD5xbbVO2drVdkvo6s,317
|
|
20
20
|
pytbox/categraf/jinja2/input.ping/ping.toml.j2,sha256=UCRy_IVoI_FDQxfsDpGS-aZJasFG1sf67ScN9U8YVIo,344
|
|
21
21
|
pytbox/categraf/jinja2/input.prometheus/prometheus.toml.j2,sha256=6ax30L1sAUreojlzf0v26GzOAiheCP0Lj7n5IIjgco0,384
|
|
22
|
+
pytbox/categraf/jinja2/input.snmp/cisco_interface.toml.j2,sha256=pamihAFrsnoKgeR6gw6i7fJYX9VnNmxiRcBdYnRsy9Y,1930
|
|
23
|
+
pytbox/categraf/jinja2/input.snmp/cisco_system.toml.j2,sha256=W0zYf1OZ_oil8n9Pe1Bhl3NtVJQPvLETv6GAp1sRVl0,914
|
|
22
24
|
pytbox/categraf/jinja2/input.snmp/h3c_interface.toml.j2,sha256=pamihAFrsnoKgeR6gw6i7fJYX9VnNmxiRcBdYnRsy9Y,1930
|
|
23
25
|
pytbox/categraf/jinja2/input.snmp/h3c_system.toml.j2,sha256=W0zYf1OZ_oil8n9Pe1Bhl3NtVJQPvLETv6GAp1sRVl0,914
|
|
26
|
+
pytbox/categraf/jinja2/input.snmp/huawei_interface.toml.j2,sha256=pamihAFrsnoKgeR6gw6i7fJYX9VnNmxiRcBdYnRsy9Y,1930
|
|
27
|
+
pytbox/categraf/jinja2/input.snmp/huawei_system.toml.j2,sha256=W0zYf1OZ_oil8n9Pe1Bhl3NtVJQPvLETv6GAp1sRVl0,914
|
|
28
|
+
pytbox/categraf/jinja2/input.snmp/ruijie_interface.toml.j2,sha256=pamihAFrsnoKgeR6gw6i7fJYX9VnNmxiRcBdYnRsy9Y,1930
|
|
29
|
+
pytbox/categraf/jinja2/input.snmp/ruijie_system.toml.j2,sha256=W0zYf1OZ_oil8n9Pe1Bhl3NtVJQPvLETv6GAp1sRVl0,914
|
|
24
30
|
pytbox/categraf/jinja2/input.vsphere/vsphere.toml.j2,sha256=7SCo8DSh5Uuy-7MeWw-soy6sqblK54k6K2WWSlimELk,8396
|
|
25
31
|
pytbox/cli/__init__.py,sha256=5ID4-oXrMsHFcfDsQeXDYeThPOuQ1Fl2x2kHWfgfOEw,67
|
|
26
32
|
pytbox/cli/main.py,sha256=S-DBp-1d0BCpvZ7jRE3bYmhKSiPpCJHFGsbdVF485BY,322
|
|
@@ -41,14 +47,18 @@ pytbox/feishu/helpers.py,sha256=jhSkHiUw4822QBXx2Jw8AksogZdakZ-3QqvC3lB3qEI,201
|
|
|
41
47
|
pytbox/feishu/typing.py,sha256=3hWkJgOi-v2bt9viMxkyvNHsPgrbAa0aZOxsZYg2vdM,122
|
|
42
48
|
pytbox/log/logger.py,sha256=7ZisXRxLb_MVbIqlYHWoTbj1EA0Z4G5SZvITlt1IKW8,7416
|
|
43
49
|
pytbox/log/victorialog.py,sha256=gffEiq38adv9sC5oZeMcyKghd3SGfRuqtZOFuqHQF6E,4139
|
|
50
|
+
pytbox/mail/alimail.py,sha256=ap6K6kmKTjqbHlfecYBi-EZtOY1iQ8tCilP8oqUz21Q,4621
|
|
51
|
+
pytbox/mail/client.py,sha256=zr51tW3URhaA1EzrXZ85Go3QIHGz_0aMszmGSFcJFLk,9065
|
|
52
|
+
pytbox/network/meraki.py,sha256=054E3C5KzAuXs9aPalvdAOUo6Hc5aOKZSWUaVbPquy4,6112
|
|
44
53
|
pytbox/utils/env.py,sha256=jO_-BKbGuDU7lIL9KYkcxGCzQwTXfxD4mIYtSAjREmI,622
|
|
45
54
|
pytbox/utils/load_config.py,sha256=wNCDPLH7xet5b9pUlTz6VsBejRsJZ7LP85wWMaITBYg,3042
|
|
46
55
|
pytbox/utils/ping_checker.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
|
|
47
56
|
pytbox/utils/response.py,sha256=kXjlwt0WVmLRam2eu1shzX2cQ7ux4cCQryaPGYwle5g,1247
|
|
48
57
|
pytbox/utils/richutils.py,sha256=OT9_q2Q1bthzB0g1GlhZVvM4ZAepJRKL6a_Vsr6vEqo,487
|
|
49
58
|
pytbox/utils/timeutils.py,sha256=XbK2KB-SVi7agNqoQN7i40wysrZvrGuwebViv1Cw-Ok,20226
|
|
50
|
-
pytbox
|
|
51
|
-
pytbox-0.1.
|
|
52
|
-
pytbox-0.1.
|
|
53
|
-
pytbox-0.1.
|
|
54
|
-
pytbox-0.1.
|
|
59
|
+
pytbox/win/ad.py,sha256=-3pWfL3dElz-XoO4j4M9lrgu3KJtlhrS9gCWJBpafAU,1147
|
|
60
|
+
pytbox-0.1.1.dist-info/METADATA,sha256=0MJBLQ3fP2OlkoGgdLPtZjmryaB8vDZ2-YbzO3f9mt4,6252
|
|
61
|
+
pytbox-0.1.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
62
|
+
pytbox-0.1.1.dist-info/entry_points.txt,sha256=YaTOJ2oPjOiv2SZwY0UC-UA9QS2phRH1oMvxGnxO0Js,43
|
|
63
|
+
pytbox-0.1.1.dist-info/top_level.txt,sha256=YADgWue-Oe128ptN3J2hS3GB0Ncc5uZaSUM3e1rwswE,7
|
|
64
|
+
pytbox-0.1.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|